接觸過緩沖區溢出的朋友對這個絕對不陌生,EIP,EBP,ESP寄存器。這裡先不解釋,先看一段代碼吧。
char a[8] = "zpf06188";
for (int i=0;i<8;i++)
{
printf("%# x \n",&a[i]);
}
在VC6.0編譯器裡面,這樣的代碼是會報一個array bounds overflow的錯,下标越界了。這是編譯器直接對數組下标的檢查,換一種方法,用拷貝函數呢?
#include<stdio.h>
#include<string.h>
char name[] = "abcdefghijklmnopqrstuvwxyz";
int main()
{
char output[8];
strcpy(output, name);
for(int i=0;i<8;i++)
printf("%# x ",output[i]);
return 0;
}
輸出結果就成了:
調試結果指向:
注意這裡的值70 6F 6E 6D 分别是什麼?對照碼表就可以查出來是ponm,當然存儲方式應該是mnop。
堆棧的存儲是低位向高位存儲,整個操作完成,main函數執行完畢之後,堆棧中的EBP,EIP要回複回去,但是由于在strcpy的時候,拷貝的字元長度已經超過了數組限定的值,這就導緻寫入的值覆寫了EIP,EBP。最後彈出EIP的時候卻發現,它原來的東西沒了,被寫成了6d6e6f70了。是以出錯了。
EBP是"基址指針"(BASE POINTER), 它最經常被用作進階語言函數調用的"架構指針"(frame pointer). 在破解的時候,經常可以看見一個标準的函數起始代碼:
ESP 專門用作堆棧指針,被形象地稱為棧頂指針,堆棧的頂部是位址小的區域,壓入堆棧的資料越多,ESP也就越來越小。在32位平台上,ESP每次減少4位元組。
EIP:寄存器存放下一個CPU指令存放的記憶體位址,當CPU執行完目前的指令後,從EIP寄存器中讀取下一條指令的記憶體位址,然後繼續執行。
棧的基本模型:
參數N | ↓高位址 |
參數… | 函數參數入棧的順序與具體的調用方式有關 |
參數 3 | |
參數 2 | |
參數 1 | |
EIP | 傳回本次調用後,下一條指令的位址 |
EBP | 儲存調用者的EBP,然後EBP指向此時的棧頂。 |
臨時變量1 | |
臨時變量2 | |
臨時變量3 | |
臨時變量… | |
臨時變量5 |
void fun(void)
{
printf("hello world");
}
void main(void)
{
fun()
printf("函數調用結束");
}