七. 讓EXE導出函數及DLL木馬

偶然發現OllyDBG.exe導出了一堆函數,這些函數都是供其插件調用的。對這種體系結構很感
興趣,想弄清楚它的實現原理。後來又看到樑肇新的書《編程高手箴言》第278頁提到的調用
門,覺得都應該差不多。

三種不同的解決辦法(原理可能是一樣的,:)):

1)在導出函數聲明之前加上__declspec(dllexport)。例:
__declspec(dllexport) int Add(int a, int b);
__declspec(dllexport) int Sub(int a, int b);
__declspec(dllexport) int Mul(int a, int b);
__declspec(dllexport) int Div(int a, int b);

2)在鏈接器參數中設置。例:
#pragma comment(linker, "/EXPORT:_Add,@1,NONAME")
#pragma comment(linker, "/EXPORT:_Sub,@2,NONAME")
#pragma comment(linker, "/EXPORT:_Mul,@3,NONAME")
#pragma comment(linker, "/EXPORT:_Div,@4,NONAME")

3)添加一個def文件,例:
EXPORTS
Add       @1 NONAME
Sub       @2 NONAME
Mul       @3 NONAME
Div       @4 NONAME
另需要在鏈接器命令行參數中指定def文件名: /DEF:Callee.def
注意:在def文件中不要有  LIBRARY [library][BASE=address]  這樣的語句。

相比較而言,後兩種方法可以設置更多的參數。


函數舉例:

extern "C"
{
/*這應在.h吧
__declspec(dllexport) int Add(int a, int b);
__declspec(dllexport) int Sub(int a, int b);
__declspec(dllexport) int Mul(int a, int b);
__declspec(dllexport) int Div(int a, int b);
*/


int Add(int a, int b)
{
    return (a + b);
}

int Sub(int a, int b)
{
    return (a - b);
}

int Mul(int a, int b)
{
    return (a * b);
}

int Div(int a, int b)
{
    if (b == 0)
      return 0;
    else
      return (a / b);
}

}

編譯時會自動生成相應的導出庫(lib)文件,供調用者使用。


調用方法和普通的動態鏈接庫調用一樣。
調用者必須能夠找到被調用者的位置,否則報錯,被調用者是否運行不影響。

調用代碼舉例:

extern "C"
{
int Add(int a, int b);
int Sub(int a, int b);
int Mul(int a, int b);
int Div(int a, int b);
}

#pragma comment (lib, "Callee.lib")

void CCallerDlg::OnBnClickedCalculate()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);

switch (((CComboBox *)GetDlgItem(IDC_COMBO_OPERATOR))->GetCurSel())
{
case ADD:
    {
      m_iResult = Add(m_iNum1, m_iNum2);
      break;
    }
case SUB:
    {
      m_iResult = Sub(m_iNum1, m_iNum2);
      break;
    }
...
...


我在OD中跟了一下,發現這跟調用動態鏈接庫也差不多。
不過那幾個函數被映射到下面的地址處:

003810F0 > 8B4424 08                 mov     eax, dword ptr [esp+8]
003810F4    8B4C24 04                 mov     ecx, dword ptr [esp+4]
003810F8    03C1                      add     eax, ecx
003810FA    C3                        retn
003810FB    CC                        int3
003810FC    CC                        int3
003810FD    CC                        int3
003810FE    CC                        int3
003810FF    CC                        int3

跟常規的動態鏈接庫被映射到高地址處略有不同。
還不知道是什麼原因。

結論:
EXE完全可以和DLL一樣導出函數,一樣被調用。

進一步的工作:
我發現這個例子跟OllyDbg.exe還是有些不同,跟“調用門”的說法也有不同。這裏實際上還是
跟DLL差不多的原理。下一步爭取實現一個跟OllyDbg.exe差不多的例子。

從前文可知,DLL在程序編制中可作出巨大貢獻,它提供了具共性代碼的複用能力。但是,正如一門高深的武學,若被掌握在正義之俠的手上,便可助其仗義江湖;但若被掌握在邪惡之徒的手上,則必然在江湖上掀起腥風血雨。DLL正是一種這樣的武學。DLL一旦染上了魔性,就不再是正常的DLL程序,而是DLL木馬,一種惡貫滿盈的病毒,令特洛伊一夜之間國破家亡。

  DLL木馬的原理

  DLL木馬的實現原理是編程者在DLL中包含木馬程序代碼,隨後在目標主機中選擇特定目標進程,以某種方式強行指定該進程調用包含木馬程序的DLL,最終達到侵襲目標系統的目的。

  正是DLL程序自身的特點決定了以這種形式加載木馬不僅可行,而且具有良好的隱藏性:

  (1)DLL程序被映射到宿主進程的地址空間中,它能夠共享宿主進程的資源,並根據宿主進程在目標主機的級別非法訪問相應的系統資源;

  (2)DLL程序沒有獨立的進程地址空間,從而可以避免在目標主機中留下"蛛絲馬跡",達到隱蔽自身的目的。

  DLL木馬實現了"真隱藏",我們在任務管理器中看不到木馬"進程",它完全溶進了系統的內核。與"真隱藏"對應的是"假隱藏","假隱藏"木馬把自己註冊成爲一個服務。雖然在任務管理器中也看不到這個進程,但是"假隱藏"木馬本質上還具備獨立的進程空間。"假隱藏"只適用於Windows9x的系統,對於基於WINNT的操作系統,通過服務管理器,我們可以發現系統中註冊過的服務。

  DLL木馬注入其它進程的方法爲遠程線程插入。

  遠程線程插入技術指的是通過在另一個進程中創建遠程線程的方法進入那個進程的內存地址空間。將木馬程序以DLL的形式實現後,需要使用插入到目標進程中的遠程線程將該木馬DLL插入到目標進程的地址空間,即利用該線程通過調用Windows API LoadLibrary函數來加載木馬DLL,從而實現木馬對系統的侵害。

  DLL木馬注入程序

  這裏涉及到一個非常重要的Windows API――CreateRemoteThread。與之相比,我們所習慣使用的CreateThread API函數只能在進程自身內部產生一個新的線程,而且被創建的新線程與主線程共享地址空間和其他資源。而CreateRemoteThread則不同,它可以在另外的進程中產生線程!CreateRemoteThread有如下特點:

  (1)CreateRemoteThread較CreateThread多一個參數hProcess,該參數用於指定要創建線程的遠程進程,其函數原型爲:

HANDLE CreateRemoteThread(
 HANDLE hProcess, //遠程進程句柄
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 SIZE_T dwStackSize,
 LPTHREAD_START_ROUTINE lpStartAddress,
 LPVOID lpParameter,
 DWORD dwCreationFlags,
 LPDWORD lpThreadId
);

  (2)線程函數的代碼不能位於我們用來注入DLL木馬的進程所在的地址空間中。也就是說,我們不能想當然地自己寫一個函數,並把這個函數作爲遠程線程的入口函數;

  (3)不能把本進程的指針作爲CreateRemoteThread的參數,因爲本進程的內存空間與遠程進程的不一樣。
發佈了27 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章