棧溢位筆記1.11 SafeSEH
在上節寫示例的過程中,我把要用到的POP+POP+RET指令寫在了自己的一個DLL中。POP+POP+RET指令是很常見的指令,一般函式的末尾都是這種形式,因此,系統DLL中應該是有該指令的,比如ntdll.dll中就有:
圖73
我們把上節示例中的地址改成這個地址試試,沒有彈出MessageBox,說明Shellcode失效了,在偵錯程式中看看:
圖74
提示說無法處理異常,說明改了地址後的異常處理函式沒有被執行。下面我們繼續用我自己的DLL,但是重新編譯它,這次在編譯時加上“/SAFESEH”選項:
圖75
再來嘗試一下(注意,重新編譯後,指令地址發生變化),還是同樣的提示。用Immunity Debugger的mona外掛檢視以下載入的模組:
圖76
只有exe不是SafeSEH,其它DLL都是,包括ntdll.dll。這就是本節的內容了。
為了防止基於SEH的棧溢位,Windows改進了SEH,稱為SafeSEH。從前面我們至少看到一點,啟用SafeSEH編譯的模組,我們的Shellcode是無法利用的,系統並不會呼叫我們偽造的這個“異常處理函式”。
事實上,啟用SafeSEH之後,模組中使用的異常處理函式都要提前註冊,註冊的地方就在載入配置目錄(Load Config Directory)。前面我們接觸過匯出表,匯入表,這個載入配置目錄也類似,屬於16中資料目錄中的一種。這樣的話,發生異常之後,呼叫異常處理函式前,系統會對異常處理函式地址進行檢驗,如果沒有註冊,說明這個異常處理函式有問題,就直接忽略該異常處理函式。就如我們前面所看到的,我們偽造的異常處理函式被忽略,後面的又被我們所覆蓋,因此異常無法被處理。
怎麼辦呢?第一個辦法如我前面所做的,如果程式中有模組沒有啟用SafeSEH,我們還是可以利用它來載入Shellcode。第二個辦法就是SafeSEH有個例外,就是如果異常處理函式位於已載入模組地址範圍之外,則異常處理函式依舊會被呼叫。這樣機會就來了,即使程式中所有模組都啟用了SafeSEH,或者雖然沒有啟用,但無法找到合適的指令塊做跳板。我們還可以嘗試其它不屬於該程序的模組。
NLS,全稱National Language Support,即本地語言支援。這個語言支援不只是文字,還有鍵盤,時間日期格式等,那麼.nls字尾的檔案就是相關的資原始檔。這些資原始檔使用的方式為記憶體對映,即讀取其記憶體空間相當於讀檔案。程序結構PEB中有相應的成員:
圖77
這些成員指標在程序初始化的時候指向資原始檔對映之後相應的地址。
需要注意的是,雖然我們在unicode.nls找到了需要的“指令”,但unicode.nls是資料,只不過是其中的資料正好和指令操作碼相同。這與我們把Shellcode放在棧上執行時相同的。