天天看點

Dll導出函數劫持通用方法問題發現劫持方法

  • 問題發現
  • 劫持方法
    • 劫持思路
    • 可能問題
    • 劫持實作
      • 導出函數清單
      • 函數上下文
      • 擷取真函數的位址
      • 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應該導出并實作十個對應的函數。

函數上下文

既然本方法是一個通用的方法,那麼自然隻需要實作一個通用的代碼流程即可。考慮到調用者調用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

繼續閱讀