1. 程式人生 > >脫殼實踐之尋找OEP——兩次內存斷點法

脫殼實踐之尋找OEP——兩次內存斷點法

ring 步驟 oca http cad 指定 技術分享 異常 所有

0x00 前言

對於加殼程序第一件事就是要找到OEPoringinal Entry point),由於加殼的緣故,當PE文件載入OD或者其他調試軟件時進入的的往往是殼程序的入口地址。所以要進行逆向分析第一步就必須找到PE程序的原始入口點。

0x01 殼的加載過程

殼和病毒在某些方面比較類似,都需要比原程序更早獲得控制權。殼修改了原程序的的執行文件的組織結構,從而比原程序更早獲得控制權,並且並不會影響原程序的正常運行。了解的殼的加載過程對於脫殼加殼異常重要。殼的加載過程如下:

1)保存入口參數

加殼程序初始化時保存各個寄存器的值,外殼執行完畢,再恢復各個寄存器內容,最後再跳到源程序的入口處執行。通常,用

pushad/popadpushfd/popfd指令來保存的和恢復現場環境。

2)獲取殼自己所需的API函數

一般的殼輸入表只有GetProAddress,GetMoudleHanleLoadLibrary這幾個API函數,甚至只有Kernel32.DLL以及GetProAddress。如果需要其他的API函數,可以通過LoadLibraryAW)或者LoadLibraryExaW)將DLL文件映像映射到調用進程的地址空間,函數返回的HINSTANCE值用於標識文件映像到虛擬內存地址。

LoadLibrary函數的原型如下:

HINSTANCE LoadLibrary{

LPCTSTR lpLibFileName //DLL文件名地址

}

返回值:成功時返回模塊句柄,失敗返回NULL

DLL文件已經被映射到調用進程的地址空間裏,可以調用GetModuleHanleAW)函數獲得DLL模塊的句柄,函數的地址原型如下:

HMODULE GetModuleHandle{

LPCTSTR lpModuleName //DLL文件地址

}

一旦模塊被加載,線程就可以調用GetProcAddress函數獲取輸入函數地址。函數的原型如下:

FARPROC GetProcAddress{

HMODULE hModlue //DLL模塊句柄

LPCSTR lpProName //函數名

}

這三個函數異常重要,對於程序加殼幫助很大。後面幾篇將會詳細介紹用法,這裏暫且羅列出來。

3)解密原程序的各個區塊的數據

殼出於保護原程序代碼和數據的目的,一般都會加密原程序文件的各個區塊,在程序時外殼將會對這些數據解密,以讓程序能夠正常運行。殼一般都是按區塊加密的,那麽在解密時也是按區塊解密的,並且把解密的區塊數據按照區塊的定義放在合適的內存位置。

4)ITA的初始化

ITA填寫,本來應該由PE加載器實現。但是由於加殼時,自己構造了一個輸入表,並且讓PE文件頭輸入表指針指向了自建的輸入表。所以PE裝載器就對自建的輸入表進行填寫。那麽原來PE輸入表只能由外殼程序來填寫了。外殼所要做的就是將這個新輸入表結構從頭到尾掃描一遍,對每一個DLL引入的所有函數重新獲取地址,並填寫在ITA表中。

5)重定位處理

文件執行時將被映像到指定的內存地址中,這個初始地址稱為基址。對於EXE文件,windows系統會盡量使用EXE問價所指定的內存地址,比如某EXE問價的基址為40000h,而運行時Windows系統提供給程序使用的基地址也是40000h。這種情況就不需要重定位了。對於DLL文件,windows沒辦法每一次提供DLL運行時提供相同的基址。對於這種情況,重定位是必須的。此時殼程序也需要提供PE文件的重定位功能。所以加殼DLL文件比加殼EXE文件多一個重定位表。

6)HOOK-API

程序文件中輸入表的作用是讓windows系統在程序運行時提供API的實際地址給程序使用。在程序的第一行代碼執行前,windows系統就完成了這項工作。

殼程序一般都修改了原程序的輸入表,然後自己模仿windows系統的工作來填充輸入表的中相關數據。在填充過程中,外殼程序可填充HOOK-API的代碼地址,這樣就可間接的獲取程序的控制權。

7)跳轉到程序入口點(OEP)

經歷過以上步驟後,外殼程序的功能就完成了,隨後他會把控制權交給原程序,一般的殼這裏會有一個明顯的“分界線”。當然現在越來越多的加密殼將OEP一段代碼搬到外殼的地址空間裏,然後將這段代碼清除掉。這種方式稱為StolenBytes。這樣,OEP與外殼就沒有明顯的分界線了,這增加了脫殼的難度。

0x02 利用兩次內存斷點法手動找到OEP

兩次內存法的原理就是利用了殼加載過程第三步時需要對各個區段進行解密並將解密後的區段寫入各個區段,完畢之後會跳轉至原程序的OEP處。當然,如果我們能判斷出殼何時跳轉至OEP處最好,但是一般這並不容易。但是我們可以先對.data區塊下斷後再運行程序(因為區段.code.data先解壓,運行到這個斷點時.code以及解密完成),隨後再對.code(有的編譯器是.text)段下斷在運行,這樣程序就會停在OEP處(因為解密完成後殼程序一定再次返回到OEP處,將控制權交給原程序)。這個方法就是兩次內存法。

0x03 實例介紹兩次斷點法找OEP過程

1)將文件拖入odalt+m進入內存模板,隨後對.data區塊按F2下斷,如下圖:

技術分享圖片

2)點擊F9運行,此時程序停在了下來,如下圖:

技術分享圖片

這裏其實馬上就要對.data區塊進行解密讀寫操作了,此時再alt+m進入內存模塊,對.text(這個就是.code區塊,由於編譯器不同,有的顯示.text區塊)區塊下斷。

3)點擊F9運行,此時程序停止,如下圖:

技術分享圖片

其實這裏就是原程序的OEP地址了,由於odPE文件進行了分析,所以顯示如上圖,我們可以右鍵,刪除模塊分析即可得到下圖:

技術分享圖片

標紅框的地方就是OEP地址了。

0x04 總結

兩次內存斷點法雖然簡單,但是我們還是要弄清楚其中的原理。它其實就是利用殼加載過程需要對區段進行解密然後返回原程序OEP這一特性

脫殼實踐之尋找OEP——兩次內存斷點法