本文講的是<b>Windows Shellcode學習筆記——通過VirtualProtect繞過DEP</b>,
0x00 前言
在掌握了棧溢出的基本原理和利用方法後,接下來就要研究如何繞過Windows系統對棧溢出利用的重重防護,是以測試環境也從xp轉到了Win7(相比xp,Win7的防護更全面)。本文将要介紹經典的DEP繞過方法——通過VirtualProtect繞過DEP。
0x01 簡介
本文将要介紹以下内容:
0x02 相關概念
DEP:
溢出攻擊的根源在于計算機對資料和代碼沒有明确區分,如果将代碼放置于資料段,那麼系統就會去執行
為了彌補這一缺陷,微軟從XP SP2開始支援資料執行保護(Data Exection Prevention)
DEP保護原理:
資料所在記憶體頁辨別為不可執行,當程式溢出成功轉入shellcode時,程式會嘗試在資料頁面上執行指令,而有了DEP,此時CPU會抛出異常,而不是去執行指令
DEP四種工作狀态:
DEP繞過原理:
如果函數傳回位址并不直接指向資料段,而是指向一個已存在的系統函數的入口位址,由于系統函數所在的頁面權限是可執行的,這樣就不會觸發DEP
也就是說,可以在代碼區找到替代指令實作shellcode的功能
但是可供利用的替代指令往往有限,無法完整的實作shellcode的功能
于是産生了一個折中方法:通過替代指令關閉DEP,再轉入執行shellcode
記憶體頁:
x86系統一個記憶體頁的大小為4kb,即0x00001000,4096
ROP:
面向傳回的程式設計(Return-oriented Programming)
VirtualProtect:
lpAddress:記憶體起始位址
dwsize:記憶體區域大小
flNewProtect:記憶體屬性,PAGE_EXECUTE_READWRITE(0x40)
lpflOldProtect:記憶體原始屬性儲存位址
通過VirtualProtect繞過DEP:
在記憶體中查找替代指令,填入合适的參數,調用VirtualProtect将shellcode的記憶體屬性設定為可讀可寫可執行,然後跳到shellcode繼續執行
0x03 VS2012的編譯配置
測試環境:
項目屬性:
具體配置方法:
配置屬性-c/c++-所有屬性
配置屬性-連結器-所有屬性
0x04 實際測試
測試1:
測試代碼:
注:
strcpy在執行時遇到0x00會提前截斷,為便于測試shellcode,将strcpy換成memcpy,遇到0x00不會被截斷

如上圖,成功将傳回位址覆寫為0x45444342
測試2:
shellcode起始位址為0x00403020
對應的機器碼為0x0059016A
将傳回位址覆寫為shellcode起始位址
shellcode實作如下操作:
其他位用0x90填充
c代碼如下:
如上圖,shellcode成功執行,ECX寄存器指派為1
測試3:
開啟DEP,再次調試,發現shellcode無法執行,如圖
測試4:
下載下傳安裝Immunity Debugger
下載下傳mona插件,下載下傳位址如下:
https://github.com/corelan/mona
将mona.py放于C:Program FilesImmunity IncImmunity DebuggerPyCommands下
啟動Immunity Debugger,打開test.exe
使用mona插件自動生成rop鍊,輸入:
如圖
mona會搜尋所有的DLL,用于構造rop鍊
執行指令後在C:Program FilesImmunity IncImmunity Debugger下生成檔案rop.txt、rop_chains.txt、rop_suggestions.txt、stackpivot.txt
檢視rop_chains.txt,會列出可用來關閉DEP的ROP鍊,選擇VirtualProtect()函數
如上圖,成功建構ROP鍊
不同環境有可能無法獲得完整參數,需要具體環境具體分析
對應的測試poc修改如下:
其中0x9059016A為PUSH 1;POP ECX;NOP;的機器碼,如果繞過DEP,該指令将會成功執行
編譯後在OllyDbg中調試
單步跟蹤到CALL KERNELBA.VirtualProtectEX,檢視堆棧
可獲得傳入的函數參數
如上圖,不巧的是shellcode覆寫了SEH鍊
這樣會導緻傳入VirtualProtectEX函數的參數不正确,調用失敗,猜測調用VirtualProtectEX函數的傳回值為0
如上圖,驗證上面的判斷,EAX寄存器表示傳回值,傳回值為0,修改記憶體屬性失敗
解決思路:
我們需要擴大棧空間,将SEH鍊下移,確定shellcode不會覆寫到SEH鍊
解決方法:
修改源代碼,通過申請空間的方式下移SEH鍊
測試5:
關鍵代碼如下:
編譯程式,再次放在OllyDbg中調試
SEH鍊成功“下移”,位于高位址,未被shellcode覆寫
此時傳入VirtualProtectEX函數的參數正确
按F8單步執行,檢視結果
如上圖,傳回值為0,修改記憶體屬性仍失敗
LastErr顯示錯誤為ERRPR_INVALID_ADDRESS(000001E7),表示位址錯誤
測試6:
檢視正常調用函數VirtualProtect()時的堆棧,對比測試5,分析失敗原因
正常調用的實作代碼如下:
測試7:
如果将起始位址修改為一個不能通路的位址,如0x40303020
編譯程式,放在OllyDbg中調試
格式如圖
如圖,産生同樣錯誤:ERRPR_INVALID_ADDRESS(000001E7)
猜測,shellcode傳入的起始位址有問題
繼續我們的測試
測試8
接着測試5,單步跟蹤到CALL KERNELBA.VirtualProtectEX,嘗試修改堆棧中的資料
将記憶體位址0x0012FF2c修改為目前記憶體頁的起始位址,即0x0012F000
如下圖,寄存器EAX的值為1,即傳回值為1,成功修改記憶體屬性
接着向下執行,在CALL ESP的位置按下F7,單步步入
如上圖,發現PUSH 1;POP ECX成功執行,測試成功,成功通過VirtualProtect繞過DEP,執行資料段的shellcode
這種情況下,VirtualProtectEX一次最大隻能修改4096長度的記憶體(即一個記憶體頁的長度),且不能跨頁修改,如果越界,傳回值為0,修改失敗
通過C調用函數VirtualProtect不存在上述問題,可跨頁,長度大于4096
0x05 小結
為了在Win7下搭建測試環境,對VS2012的編譯配置需要特别注意,多重保護在提高程式安全性的同時也給環境搭建帶來了麻煩
不同系統下可供使用的替代指令往往不同,需要不斷變換思路,構造合适的ROP鍊
另外,Immunity Debugger的mona插件可為ROP鍊的編寫提供便利,但要注意存在bug的情況,需要更多的測試和優化
如果shellcode長度大于4096,使用VirtualProtect關閉DEP會失敗,需要選擇其他方法
原文釋出時間為:2017年3月24日
本文作者:3gstudent
本文來自雲栖社群合作夥伴嘶吼,了解相關資訊可以關注嘶吼網站。
<a href="http://www.4hou.com/technology/3943.html" target="_blank">原文連結</a>