- 問題發現
- 劫持方法
- 劫持思路
- 可能問題
- 劫持實作
- 導出函數清單
- 函數上下文
- 擷取真函數的位址
- eax值的儲存
- 完整代碼
問題發現
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