棧溢出實(shí)驗(yàn)
shellcode初探
操作系統(tǒng):Windows XP SP3
開發(fā)環(huán)境:VC++ 6.0
調(diào)試器:Ollydbg
#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[44];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
LoadLibrary("user32.dll");
if(!(fp=fopen("password.txt","rw+")))
{
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!n");
}
else
{
printf("Congratulation! You have passed the verification!n");
}
fclose(fp);
}
程序未對輸入的密碼進(jìn)行長度檢測,接收密碼的緩沖區(qū)只有8,而輸入的密碼最長可以輸入1024。判斷密碼是否正確的變量authenticated存儲在棧中,當(dāng)輸入的密碼長度大于8時,輸入的字符串將沖破緩沖區(qū),淹沒authenticated所處的位置。當(dāng)密碼錯誤時authenticated的值是1,正確的時候authenticated的值是0.這就意味著我們可以構(gòu)造一個合適的輸入字符串來改變判斷結(jié)果。
本次的程序與上一節(jié)的程序區(qū)別在引入了windows頭文件,用于使用LoadLibrary函數(shù)。另外緩沖區(qū)的長度從8增加為44。
本次實(shí)戰(zhàn)著重初步植入簡單的shellcode代碼。
1. 本次實(shí)驗(yàn)的最終目標(biāo)是在成功溢出后彈出一個消息框。那么首先我們要找到消息框的API地址,如果考慮通用性,那么需要一套負(fù)責(zé)的過程,現(xiàn)在我們先不考慮這些。先使用Windows自帶的工具獲取API的地址。
2. 我們需要用到的API是MessageBox,熟悉Windows編程的朋友應(yīng)該知道,實(shí)際上并沒有MessgaeBox這個API,我們實(shí)際上需要調(diào)用的是MessageBoxA或者M(jìn)essageBoxW,這取決于我們使用的環(huán)境是多字節(jié)還是寬字符。此次我們那使用的是MessageBoxA,這個API處于USER32.DLL中,我們獲取一下USER.DLL基址是0x77D10000,然后獲取MessageBoxA的偏移是0x407EA,兩者相加就可以獲取到MessageBoxA在內(nèi)存中的入口地址0x77D507EA。
(此處獲取到的地址只在我的計算機(jī)上生效。如果本地實(shí)驗(yàn),需要本地重新獲取。)
3. 此時我們需要確定一下我們需要覆蓋的返回地址的位置和BUFFER起始位置。已知緩沖區(qū)長度為44,那么第45個字節(jié)的00將會覆蓋掉密碼比對結(jié)果。為了方便查看,我們使用abcd作為輸入,長度為44的緩沖區(qū)則需要11組abcd剛好覆蓋。
4. 運(yùn)行起來,進(jìn)入到密碼比對函數(shù)。執(zhí)行strcpy后觀察堆棧的情況??梢钥吹骄彌_區(qū)起點(diǎn)是0x0012FAF0,返回地址是0x0012FB24。
5. 那么我們是不是可以這樣玩,把返回地址淹沒成緩沖區(qū)起點(diǎn),這樣指令就會從緩沖區(qū)起點(diǎn)開始執(zhí)行。這樣我們就需要把返回地址覆蓋成0x0012FAF0,然后再構(gòu)造一個彈出消息框的機(jī)器碼用于彈出消息框。
5.1 淹沒返回值為0x0012FAF0
5.2這里我們先不討論shellcode的編寫。這會在接下來的大章節(jié)中詳細(xì)描述。我們直接把彈出消息框的機(jī)器碼填在password中。
5.3進(jìn)入shellcode執(zhí)行
5.4溢出成功