1. 程式人生 > >Dll匯出函式劫持通用方法

Dll匯出函式劫持通用方法

問題發現

dll劫持是一種常見的攻擊方法,但是也可以用在不知道程式原始碼的情況下除錯dll的函式。之前在滴水教程的視訊中注意到一個問題,視訊作者演示了一個劫持messagebox函式,列印輸出引數的過程,當時學生提問是否存在一種通用的方法可以劫持所有的函式,當時視訊作者回答是無法做到。最近有些空閒想到了這個問題,覺得從模組化的角度來講,應該存在一種可以劫持所有dll函式的方法。所以在此嘗試,嘗試後發現了一種偽造dll的通用方法,使用這中方法,可以通過指令碼的方式,實現任意一個dll的偽造。

劫持方法

劫持思路

本文采取的方式為新建一個dll,新建的dll滿足一下特點:

1.匯出原dll所有的函式
2. dll的名稱和原dll相同
3. 所有原dll的匯出函式都有對應的實現

這樣在應用程式中就無法分辨自己載入的dll是原本合法的還是偽造的。當然,此方法僅限於不檢查簽名的dll。

可能問題

1.函式的返回型別未知

個人見解:從彙編的角度講,函式的返回型別在組合語言的執行過程中是未知的,返回值大都儲存在eax中,主程式則根據設定的不同,去使用函式返回的eax的值。因此,我們如果在自己的函式中,讓真正的函式去執行,那麼返回值就無所謂了。
2.函式的引數未知

個人見解:從彙編的角度講,函式的引數列表只是在呼叫的時候,為呼叫者提供一個壓棧的順序。在函式呼叫的過程中,函式呼叫者負責壓棧引數,函式的執行者只需要依據呼叫約定不同,做到保持堆疊平衡和按順序使用引數就可以。所以,只要我們在我們的函式最後呼叫真正的函式,那麼就不會影響到原本程式的堆疊空間和執行流程。

劫持實現

匯出函式列表

在實驗中,我使用了lordpe這個應用的procs.dll。該函式的匯出函式列表為:

這裡寫圖片描述
因此,我們的dll應該匯出並實現十個對應的函式。

函式上下文

既然本方法是一個通用的方法,那麼自然只需要實現一個通用的程式碼流程即可。考慮到呼叫者呼叫dll中的程式時,堆疊的分佈情況如下所示:

返回地址
引數1
引數2
…
引數n

因此我們在呼叫真正的方法之前,必須要將堆疊等資訊恢復到這個狀態。

所以此處我們選擇pushad、pushfd、popfd、popad來對環境進行儲存和恢復。

獲取真函式的地址

並且,我們需要獲取到原本函式的位置,用來跳轉,因此我們需要使用getprocaddress函式以及對應的函式名稱。通過對dll的匯出表進行,遍歷,獲取名稱十分的簡單。我們可以定一個全域性變數,用於存放每一個函式的名稱。如下:

char funname1[] = "GetModuleHandleEx";
DWORD (DWORD)GetProcAddress(LoadLibraryA("kernel32.dll"), "GetProcAddress");//獲取GetProcAddress函式的地址
hm = LoadLibraryA("PROCSold.DLL");//載入原dll,寫在dllmain中

eax值的儲存

由於要使用popad,那麼運算得到的eax的值就會同樣被覆蓋,所以我們需要將eax的值傳送出去。
這裡我們通過將eax的值mov到pushad的時候eax被壓棧的地方即可,我們在popad命令執行之前,增加語句

mov[esp+28],eax;

這樣eax的值就被儲存起來了。

完整程式碼

程式碼已上傳至github,有興趣可以訪問我們github。上傳的程式碼只限於提供一個劫持的實驗,具體應用則需要編寫一個指令碼,這裡就不提供指令碼了。
https://github.com/guanginuestc/DLL-hijacking