Windows Dll注入與API HOOK

DLL注入:

1.  使用註冊表注入dll

HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows\AppInit_DLLs

AppInit_Dlls中設置待注入的dll絕對路徑

LoadAppInit_Dlls值設爲1

2.  使用Windows掛鉤注入dll

需要使用SetWindowsHookEx函數,MSDN定義如下:

HHOOK WINAPI SetWindowsHookEx(
 _In_ int      idHook,
 _In_ HOOKPROC  lpfn,
 _In_ HINSTANCE hMod,
 _In_ DWORD     dwThreadId
);
第一個參數爲掛鉤類型;

第二個參數爲一個函數地址,即掛鉤類型事件發生時,系統應該調用的函數;

第三個參數標識一個dll,這個dll中包含第二個參數表示的函數;

第四個參數表示要給哪個線程掛鉤,0表示所有運行線程

以下給出一個示例:

HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL; 
static HHOOK hhookSysMsg; 
 
hinstDLL = LoadLibrary(TEXT("c:\\myapp\\sysmsg.dll")); 
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); 

hhookSysMsg = SetWindowsHookEx( 
                    WH_SYSMSGFILTER,
                    hkprcSysMsg,
                    hinstDLL,
                    0); 
可以使用UnhookWindowsHookEx取消掛鉤:

BOOL WINAPI UnhookWindowsHookEx(
  _In_ HHOOK hhk
);

3.  使用遠程線程注入dll

Dll注入的本質即讓目標進程中的一個線程通過LoadLibrary()加載待注入dll文件。

通常情況下,無法控制目標進程線程,此時可以創建一個線程了實現上述目的,CreateRemoteThread便提供了此功能。

HANDLE WINAPI CreateRemoteThread(
  _In_  HANDLE                 hProcess,
  _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_  SIZE_T                 dwStackSize,
  _In_  LPTHREAD_START_ROUTINE lpStartAddress,
  _In_  LPVOID                 lpParameter,
  _In_  DWORD                  dwCreationFlags,
  _Out_ LPDWORD                lpThreadId
);
hProcess表示待注入的目標進程;

lpStartAddress表示線程函數,此處設置爲LoadLibrary();

lpParameter表示傳遞給線程函數的參數,此時設置爲待注入的dll文件絕對路徑。
使用CreateRemoteThread具體注入過程如下:

(1) 使用VirtualAllocEx在遠程進程中申請一塊內存空間;

(2) 使用WriteProcessMemory將Dll路徑名複製到(1)中申請的地址;

(3) 使用GetProcAddress獲取LoadLibraryW或LoadLibraryA的實際地址;

(4) 使用CreateRemoteThread在遠程進程中創建新的線程,新線程調用LoadLibrary,並在參數中傳入(1)中申請的內存地址,此時dll文件已經注入到遠程線程,DllMain將執行我們設計的代碼。

此時遠程線程中保留一塊申請的內存空間,需要對其進行釋放:

(5) 使用VirtualFreeEx釋放(1)中申請的內存;

(6) 使用GetProcAddress獲取FreeLibrary函數實際地址;

(7) 使用CreateRemoteThread創建新的線程,調用FreeLibrary,參數傳入已注入Dll的HMODULE.

4.  使用木馬dll注入dll

將進程需要加載的dll文件替換掉,實現dll劫持。

例如,進程需要使用lpk.dll,通過僞造lpk.dll文件,利用windows應用程序加載dll的順序,讓其優先加載僞造的lpk.dll文件。

5.  把dll作爲調試器注入

(1) DebugActiveProcess(pid)附加進程;

(2) Debug循環函數:WaitForDebugEvent等待調試事件,針對不同類型進行處理;

(3) ReadProcessMemory, WriteProcessMemory操作目標進程,鉤取指定API

6.  使用CreateProcess注入代碼

該方法用於向子進程注入代碼:

(1) 創建子進程並掛起;

(2) 獲取主線程內存地址;

(3) 保存主線程起始地址指令;

(4) 插入指令,調用LoadLibrary加載dll

(5) 子進程恢復運行;

(6) 執行插入的指令;

(7) 恢復原指令,按照原來的邏輯繼續執行。

 

API 攔截:

1.  通過覆蓋代碼攔截API

(1) 獲取待攔截函數地址;

(2) 保存該函數初始幾個字節指令;

(3) 使用JMP替換這些指令,讓其跳轉到自定義的函數中,需要注意的是,自定義函數需與原函數具有相同的簽名,相同的參數,相同的返回值,相同的調用約定;

(4) 程序調用被攔截函數時,將跳轉到自定義函數中執行;

(5) 執行完畢後,將保存的原指令恢復到起始地址,調用函數讓其按照原來的邏輯執行。

2.  通過修改IAT攔截API

  每一個導入的dll文件對應一個IMAGE_IMPORT_DESCRIPTOR結構:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
其中成員變量FirstThunk指向一個IMAGE_THUNK_DATA結構體數組,數組中每一項對應一個導入函數:

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32              IMAGE_THUNK_DATA;
typedef PIMAGE_THUNK_DATA32             PIMAGE_THUNK_DATA;
因此,IAT HOOK的主要思路爲:

(1) 遍歷IMAGE_IMPORT_DESCRIPTOR數組,找到name爲需要hook的dll;

(2) 遍歷FirstThunk指向的IMAGE_THUNK_DATA,判斷Function是否爲待hook的API,若是,則替換爲新的API.

以下是《Windows核心編程》中作者給出的實現函數:

void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,   //被調模塊
                        PROC pfnCurrent,          //待HOOK原函數
						PROC pfnNew,              //替換新函數 
						HMODULE hmodCaller        //調用新函數的模塊
						) {

   // Get the address of the module's import section
   ULONG ulSize;

   // An exception was triggered by Explorer (when browsing the content of 
   // a folder) into imagehlp.dll. It looks like one module was unloaded...
   // Maybe some threading problem: the list of modules from Toolhelp might 
   // not be accurate if FreeLibrary is called during the enumeration.
   PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
   __try {
      pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(
         hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
   } 
   __except (InvalidReadExceptionFilter(GetExceptionInformation())) {
      // Nothing to do in here, thread continues to run normally
      // with NULL for pImportDesc 
   }
   
   if (pImportDesc == NULL)
      return;  // This module has no import section or is no longer loaded

   // Find the import descriptor containing references to callee's functions
   for (; pImportDesc->Name; pImportDesc++) {
      PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
      if (lstrcmpiA(pszModName, pszCalleeModName) == 0) {

         // Get caller's import address table (IAT) for the callee's functions
         PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) 
            ((PBYTE) hmodCaller + pImportDesc->FirstThunk);

         // Replace current function address with new function address
         for (; pThunk->u1.Function; pThunk++) {

            // Get the address of the function address
            PROC* ppfn = (PROC*) &pThunk->u1.Function;

            // Is this the function we're looking for?
            BOOL bFound = (*ppfn == pfnCurrent);
            if (bFound) {
               if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, 
                    sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) {
                  DWORD dwOldProtect;
                  if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY, 
                     &dwOldProtect)) {
                     WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, 
                        sizeof(pfnNew), NULL);
                     VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect, 
                        &dwOldProtect);
                  }
               }
               return;  // We did it, get out
            }
         }
      }  // Each import section is parsed until the right entry is found and patched
   }
}


發佈了42 篇原創文章 · 獲贊 5 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章