1. 程式人生 > >[DLL劫持] 1 DLL劫持之DLL基礎(1)

[DLL劫持] 1 DLL劫持之DLL基礎(1)

該系列文章是依據本人平時對動態連結庫的學習,歸納總結,所做的學習筆記。如有錯誤或待改善之處,請留下您寶貴的意見或建議。

最近在研究逆向工程的相關知識,主要用到的是C++逆向,工具有IDAOLLYDBG等,學了有一段時間了,一直苦於總結能力不夠,不知道逆向的東西該怎麼總結,所以一直都沒有總結之。這其中有用到DLL劫持技術的,覺得這個易於總結,所以先寫幾篇文章總結一下DLL劫持的技術吧。剛入門,所以只能膚淺的講解一個大概,欲深入瞭解,請百度之。

首先講一下DLL的原理,然後在用一個簡單的例子講講DLL劫持。

一.DLL的概念

DLLDynamic LinkableLibrary)是一個包含可由多個程式同時使用的程式碼和資料的庫,

DLL不是可執行檔案。簡言之,就是一種倉庫,它提供了一些變數、函式和類等,在這種倉庫的發展過程中,經歷了:無庫->靜態諒解庫->動態連結庫的過程。

靜態庫和動態庫都是程式碼共享的方式。所不同的是,靜調庫(lib)中的程式碼將會包含到最終生成的EXE中,而DLL則不必包含在最終生成的EXE檔案中,EXE執行是可以“動態”地載入和解除安裝這個與EXE獨立的DLL檔案。靜態連結庫和動態連結庫的另外一個區別在於靜態連結庫中不能再包含其他的動態連結庫或者靜態庫,而在動態連結庫中還可以再包含其他的動態或靜態連結庫。

對於動態庫,還需瞭解:

1.   DLL的編制與具體的程式語言及編譯器無關

只要遵循約定的

DLL介面規範和呼叫方式,用各種語言編寫的DLL都可以相互呼叫。譬如Windows提供的系統DLL(其中包括了WindowsAPI),在任何開發環境中都能被呼叫,不在乎其是C#Visual C++還是Delphi

2.   VC動態連結庫的分類

Visual C++支援三種DLL,它們分別是Non-MFCDLL(非MFC動態庫)、MFCRegular DLLMFC規則DLL)、MFC ExtensionDLLMFC擴充套件DLL)。

MFC動態庫不採用MFC類庫結構,其匯出函式為標準的C介面,能被非MFCMFC編寫的應用程式所呼叫;MFC規則DLL包含一個繼承自CWinApp

的類,但其無訊息迴圈;MFC擴充套件DLL採用MFC的動態連結版本建立,它只能被用MFC類庫所編寫的應用程式所呼叫。

二. Non-MFC DLL的編寫

1.   編寫

如下編寫一個Add函式,使用動態連結庫提供函式的呼叫。

VS2010中,新建一個專案,依次選擇:VisualC++ -> Win32 -> Win32控制檯應用程式,名稱為AddDLL,在應用程式設定中,選擇DLL和空專案,完成。

在建立的工程中新增lib.hlib.cpp檔案,原始碼如下:

lib.h

#ifndef LIB_H
#define LIB_H

extern "C" int __declspec(dllexport)add(int x, int y);

#endif

lib.cpp

#include "lib.h"
int add(int x, int y){
    return x + y;
}

然後就可以生成名為AddDLL.dll和AddDLL.lib的檔案了。

2.     使用(動態呼叫)

DLL的呼叫分為兩種:

(1)       由“LoadLibrary-GetProcAddress-FreeLibrary”系統Api提供的三位一體“DLL載入-DLL函式地址獲取-DLL釋放”方式,這種呼叫方式稱為DLL動態呼叫。動態呼叫方式的特點是完全由程式設計者用API函式載入和解除安裝DLL,程式設計師可以決定DLL檔案何時載入或不載入,顯式連結在執行時決定載入哪個DLL檔案。

(2)       與動態呼叫方式相對應的就是靜態呼叫方式,靜態呼叫方式的特點是由編譯系統完成對DLL的載入和應用程式結束時DLL的解除安裝。當呼叫某DLL的應用程式結束時,若系統中還有其它程式使用該DLL,則WindowsDLL的應用記錄減1,直到所有使用該DLL的程式都結束時才釋放它。靜態呼叫方式簡單實用,但不如動態呼叫方式靈活。

下面我們先來講解一下動態呼叫方式:

建立一個與DLL工程處於同一工作區的應用工程dllCall,它呼叫DLL中的函式add,其原始碼如下:
#include <windows.h>
typedef int(*lpAddFun)(int, int);//巨集定義函式指標型別

int main(int argc, char *argv[]){
   HINSTANCE hDll; //DLL控制代碼 
   lpAddFun addFun; //函式指標
   hDll = LoadLibrary("..\\Debug\\dllTest.dll");
   if (hDll != NULL){
      addFun = (lpAddFun)GetProcAddress(hDll, "add");
      if (addFun != NULL){
         int result = addFun(2, 3);
         printf("%d", result);
      }
   FreeLibrary(hDll);
   }
return 0;
}

分析上述程式碼,我們看到函式add的宣告前面添加了__declspec(dllexport)語句。這個語句的含義是宣告函式addDLL的匯出函式。DLL內的函式分為兩種:

(1)DLL匯出函式,可供應用程式呼叫;

(2)DLL內部函式,只能在DLL程式使用,應用程式無法呼叫它們。

首先,語句typedefint ( * lpAddFun)(int,int)定義了一個與add函式接受引數型別和返回值均相同的函式指標型別。隨後,在main函式中定義了lpAddFun的例項addFun

其次,在函式main中定義了一個DLLHINSTANCE控制代碼例項hDll,通過Win32 Api函式LoadLibrary動態載入了DLL模組並將DLL模組控制代碼賦給了hDll

再次,在函式main中通過Win32 Api函式GetProcAddress得到了所載入DLL模組中函式add的地址並賦給了addFun。經由函式指標addFun進行了對DLLadd函式的呼叫;

最後,應用工程使用完DLL後,在函式main中通過Win32 Api函式FreeLibrary釋放了已經載入的DLL模組。

通過這個簡單的例子,我們獲知DLL定義和呼叫的一般概念:

(1)DLL中需以某種特定的方式宣告匯出函式(或變數、類);

(2)應用工程需以某種特定的方式呼叫DLL的匯出函式(或變數、類)。

下面我們來對“特定的方式進行”闡述。

DLL中匯出函式的宣告有兩種方式:一種為以上例子中給出的在函式宣告中加上__declspec(dllexport);另外一種方式是採用模組定義(.def)檔案宣告,.def檔案為連結器提供了有關被連結程式的匯出、屬性及其他方面的資訊,這裡不做詳細的解釋。

3.     使用(靜態呼叫)

以上一節講了DLL的動態呼叫,現在我們來講解一下靜態呼叫。

將編譯AddDLL工程所生成的.lib.dll檔案拷入dllCall工程所在的路徑,dllCall執行下列程式碼:

//.lib檔案中僅僅是關於其對應DLL檔案中函式的重定位資訊
extern "C" __declspec(dllimport) add(int x,int y); 
int main(int argc, char* argv[])
{
   int result = add(2,3); 
   printf("%d",result);
   return 0;
}

由上述程式碼可以看出,靜態呼叫方式的順利進行需要完成兩個動作:

(1)告訴編譯器與DLL相對應的.lib檔案所在的路徑及檔名,#pragmacomment(lib,"dllTest.lib")就是起這個作用。

程式設計師在建立一個DLL檔案時,聯結器會自動為其生成一個對應的.lib檔案,該檔案包含了DLL 匯出函式的符號名及序號(並不含有實際的程式碼)。在應用程式裡,.lib檔案將作為DLL的替代檔案參與編譯。

(2)宣告匯入函式,extern"C" __declspec(dllimport) add(int x,int y)語句中的__declspec(dllimport)發揮這個作用。

靜態呼叫方式不再需要使用系統API來載入、解除安裝DLL以及獲取DLL中匯出函式的地址。這是因為,當程式設計師通過靜態連結方式編譯生成應用程式時,應用程式中呼叫的與.lib檔案中匯出符號相匹配的函式符號將進入到生成的EXE 檔案中,.lib檔案中所包含的與之對應的DLL檔案的檔名也被編譯器儲存在 EXE檔案內部。當應用程式執行過程中需要載入DLL檔案時,Windows將根據這些資訊發現並載入DLL,然後通過符號名實現對DLL 函式的動態連結。這樣,EXE將能直接通過函式名呼叫DLL的輸出函式,就象呼叫程式內部的其他函式一樣。

這一節就總結到此,下一節,講講DLLMain入口函式。