1.C語言變量的分布 :
C 語言有全局變量(Global)冀墨、本地變量(Local)闸衫,靜態(tài)變量(Static)、寄存器變量(Regeister)诽嘉。每種變量都有不同的分配方式蔚出。先來看下面這段代碼:
#include <stdio.h>
int g1=0, g2=0, g3=0;
int main()
{
static int s1=0, s2=0, s3=0;
int v1=0, v2=0, v3=0;
//打印出各個變量的內(nèi)存地址
printf("0x%08x\n",&v1); //打印各本地變量的內(nèi)存地址
printf("0x%08x\n",&v2);
printf("0x%08x\n\n",&v3);
printf("0x%08x\n",&g1); //打印各全局變量的內(nèi)存地址
printf("0x%08x\n",&g2);
printf("0x%08x\n\n",&g3);
printf("0x%08x\n",&s1); //打印各靜態(tài)變量的內(nèi)存地址
printf("0x%08x\n",&s2);
printf("0x%08x\n\n",&s3);
system("pause");
return 0;
}
可以看出本地變量和全局/靜態(tài)變量的分布完全不同弟翘,相差甚遠,這是因為他們分布在不同類型的區(qū)域骄酗。
進程的內(nèi)存空間分為:代碼區(qū)稀余,靜態(tài)數(shù)據(jù)區(qū)和動態(tài)數(shù)據(jù)區(qū)。全局和靜態(tài)變量分配在靜態(tài)數(shù)據(jù)區(qū)趋翻,本地變量分配在動態(tài)數(shù)據(jù)區(qū)睛琳,即”堆棧“踏烙,
2. 棧的存儲
#include <stdio.h>
void __stdcall func(int param1,int param2,int param3)
{
int var1=param1;
int var2=param2;
int var3=param3;
printf("0x%08x\n",¶meter1); //打印出各個變量的內(nèi)存地址
printf("0x%08x\n",¶meter2);
printf("0x%08x\n\n",¶meter3);
printf("0x%08x\n",&var1);
printf("0x%08x\n",&var2);
printf("0x%08x\n\n",&var3);
return;
}
int main()
{
func(1,2,3);
return 0;
}
函數(shù)的參數(shù)是從右向左傳遞师骗,即先壓棧parameter 3,然后parameter 2讨惩,最后才是parameter 1辟癌,然后是函數(shù)的返回地址,然后就是本地變量var1步脓,var2,var3
3.程序進入main()函數(shù) 愿待,棧幀的保存和關閉
例如:
int main()
{
return0;
}
匯編代碼為:
push ebp; 保存進入main()函數(shù)時其他初始化函數(shù)的棧底
move ebp,esp; 把當前esp的值作為棧底
sub esp ,40h 開辟椦セ迹空間仍侥,作為局部變量的存儲空間
push ebx
push esi
push edi 保存寄存器的值
LEA edi ,[ebp-40h] 取出此函數(shù)可用棧空間首地址
mov ecx,10h 設置ecx寄存器的值
mov eax ,occcccccch 把局部變量初始化為0xcccccccch
rep stos dword ptr [edi] 根據(jù)ecx的值鸳君,把eax的內(nèi)容农渊,以四字節(jié)為單位寫到edi指向的內(nèi)存
xor eax,eax 設置返回值為0
pop edi
pop esi
pop ebx 彈出壓入寄存器的值
add esp,40h 降低esp,局部空間釋放
cmp ebp,esp 檢查棧平衡
call _chkesp() 進入棧錯誤檢查函數(shù)
mov esp.ebp 還原esp
pop ebp 還原ebp
ret
4. 簡單的分配棧幀及溢出修改相鄰變量
例如:
#include <windows.h>
#define PASSWORD "1234567"
int verify_password(char *password){
int authenticated;
char buffer[8];
authenticated = strcmp(password,PASSWORD);
strcpy(buffer,password);
return authenticated;
}
int main(int argc, char* argv[])
{
int valid_flag = 0;
char password[1024];
FILE *fp;
if (!(fp=fopen("password.txt","rw+"))){
return 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");
}
Sleep(-1);
return 1;
}
用OD調(diào)試:
進入main()主函數(shù),找到驗證密碼的函數(shù)調(diào)用位置或颊,進入到函數(shù)具體代碼處:
前面部分就是棧分配局部變量空間和初始化的過程砸紊,然后就是字符串的計較,最后是字符串的復制囱挑,分析可得棧溢出在這一部分醉顽,在指令008D1409處把函數(shù)的返回值(EAX儲存的是返回值)存在了EBP-0XC處,下面就是strocpy的操作平挑,char buffer[8]分配了八個字節(jié)的存儲空間游添,但是password.txt的密碼如圖為24個字節(jié),知錯執(zhí)行strcpy的時候通熄,把buffer 附近的變量空間也給覆蓋了唆涝,比如返回值的。以上過程如圖所示