Windows栈溢出原理
棧是一種運算受限的線性表
其限制是僅允許在表的一端進行插入和刪除運算
這一端稱為棧頂(TOP),相對的另一端稱為棧底(BASE)
向一個棧插入新元素,稱作進棧、入棧或壓棧(PUSH)
它是把新元素放到棧頂元素的上邊,使之成為新的棧頂元素;
從一個棧刪除元素,又稱出棧或退棧(POP)
它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素
進程使用的內(nèi)存可以分成4個部分
代碼區(qū):存儲二進制機器碼,存儲器在這里取指令
數(shù)據(jù)區(qū):用于存儲全局變量
堆區(qū):動態(tài)分配和全局變量
棧區(qū):動態(tài)存儲函數(shù)間的調(diào)用關(guān)系,保證被調(diào)用函數(shù)返回時恢復(fù)到母函數(shù)中繼續(xù)運行
寄存器與函數(shù)棧幀
ESP:棧頂指針寄存器,永遠指向系統(tǒng)棧頂
EBP:基址指針寄存器,永遠指向系統(tǒng)棧最上邊一個棧的棧底
ESP和EBP之間的內(nèi)存空間為當前棧幀
2.棧的溢出
棧溢出是由于C語言系列沒有內(nèi)置檢查機制來確保復(fù)制到緩沖區(qū)的數(shù)據(jù)不得大于緩沖區(qū)的大小
因此當這個數(shù)據(jù)足夠大的時候,將會溢出緩沖區(qū)的范圍
?
3.如何利用
通過程序的緩沖區(qū)寫超出其長度的內(nèi)容,造成緩沖區(qū)的溢出,
從而破壞程序的堆棧,使程序轉(zhuǎn)而執(zhí)行其它指令,以達到攻擊的目的
造成緩沖區(qū)溢出的原因是 程序中沒有仔細檢查用戶輸入的參數(shù)
覆蓋鄰接變量
例如buffer大小是8字節(jié)
輸入8個字符,加上字符串截斷字符NULL字符,即可覆蓋相鄰變量,改變程序運行流程
修改函數(shù)返回地址
上述覆蓋相鄰變量的方法雖然很管用,但是漏洞利用對代碼環(huán)境很苛刻
更通用的攻擊緩沖區(qū)的方法是,瞄準棧幀最下方EBP和函數(shù)返回地址等棧幀的狀態(tài)值
如果繼續(xù)增加輸入字符,超出buffer[8]字符邊界
將依次淹沒 相鄰變量、前棧幀EBP、返回地址
4.實例
1)創(chuàng)建一個password.txt文件,內(nèi)容為1234
2)C語言實例代碼
代碼環(huán)境
| 編譯器 | Visual C++ 6.0 |
| 編譯選項 | 默認編譯選項 |
| build版本 | debug版本 |
運行測試一下,更改密碼文件對比結(jié)果
根據(jù)函數(shù)棧溢出原理,實現(xiàn)棧溢出需要以下過程
(1) 分析并調(diào)試程序,獲得淹沒返回地址的偏移
(2) 獲得buffer的起始地址,根據(jù)獲得的偏移將其覆蓋返回地址,使得函數(shù)返回時執(zhí)行buffer起始地址保存的代碼
(3) 提取彈框操作的機器碼并保存于buffer的起始地址處,在函數(shù)返回時得到執(zhí)行
為什么會覆蓋?
如果在password.txt中寫入恰好44個字符,那么第45個隱藏的截斷符 null 將沖刷
變量authenticated低字節(jié)中的 1,從而突破密碼驗證的限制
出于字節(jié)對齊、容易辨認的目的,我們把"4321"作為一個輸入單元
buffer[44]共需要11個這樣的單元
第12個輸入單元將authenticated覆蓋;
第13個單元將前棧幀EBP的值覆蓋;
第14個單元將返回地址覆蓋;
調(diào)試棧的布局
通過動態(tài)調(diào)試,可以得到以下信息
(1) buffer數(shù)組的起始地址為:0x0012FAF0
(2) password.txt 文件中第53~56個字符的ASCII碼值,將寫入棧幀中的返回地址,成為函數(shù)返回后執(zhí)行的指令地址
也就是說,在buffer的起始地址寫入password.txt文件中的第53~56個字節(jié)
在 verify_password 函數(shù)返回時,會跳到我們輸入的字符串開始取指執(zhí)行
(3) 給password.txt中植入機器碼,彈出消息框
MessageBoxA是動態(tài)鏈接庫user32.dll的導(dǎo)出函數(shù),本實驗中未默認加載
在匯編語言中調(diào)用這個函數(shù)需要獲得這個函數(shù)的入口地址。
獲取彈窗函數(shù)入口參數(shù)信息
MessageBoxA的入口參數(shù)可以通過user32.dll 在系統(tǒng)中加載的基址和MessageBoxA在庫中的偏移得到。
用VC6.0自帶的小工具"Dependency Walker"可以獲得這些信息(可在Tools目錄下找到)
隨便把一個有GUI界面的程序扔進去,結(jié)果如圖所示
user32.dll的基址為:0x77D10000
MessageBoxA 的偏移地址為:0x000407EA
基址+偏移地址=MessageBoxA內(nèi)存中的入口地址:0x77D507EA
我們要彈窗的字符設(shè)成"wintry",用python轉(zhuǎn)換成16進制的ASCII
然后借助OD寫匯編代碼,獲得機器碼
將上邊的機器碼,以十六進制形式逐字寫入到 password.txt
第53~56字節(jié)填入buffer的起址:0x0012FAF0 ,其余字節(jié)用 90(nop) 填充
上邊的機器碼可能是字符沒對齊的原因,會彈出內(nèi)存讀取錯誤,把字符串改為"wintry00"
成功彈出窗口
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/wintrysec/p/10616793.html
總結(jié)
以上是生活随笔為你收集整理的Windows栈溢出原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React状态管理大乱斗,横向对比Dva
- 下一篇: java信息管理系统总结_java实现科