DLL的遠程注入技術及注入dll函數調用

本文轉載自:

https://blog.csdn.net/xuplus/article/details/36051337


一、DLL注入

DLL的遠程注入技術是目前Win32病毒廣泛使用的一種技術。使用這種技術的病毒體通常位於一個DLL中,在系統啓動的時候,一個EXE程序會將這個DLL加載至某些系統進程(如Explorer.exe)中運行
這樣一來,普通的進程管理器就很難發現這種病毒了,而且即使發現了也很難清除,因爲只要病毒寄生的進程不終止運行,那麼這個DLL就不會在內存中卸載,
用戶也就無法在資源管理器中刪除這個DLL文件,真可謂一箭雙鵰哉。記得2003年QQ尾巴病毒肆虐的時候,就已經有些尾巴病毒的變種在使用這種技術了。
到了2004年初,我曾經嘗試着仿真了一個QQ尾巴病毒,但獨是跳過了DLL的遠程加載技術。直到最近在學校論壇上看到了幾位朋友在探討這一技術,便忍不住將這一塵封已久的技術從我的記憶中揀了出來,以滿足廣大的技術愛好者們。
在閱讀本文之前,你需要了解以下幾個API函數:

  ·OpenProcess - 用於打開要寄生的目標進程。

  ·VirtualAllocEx/VirtualFreeEx - 用於在目標進程中分配/釋放內存空間

  ·WriteProcessMemory - 用於在目標進程中寫入要加載的DLL名稱

  ·CreateRemoteThread - 遠程加載DLL的核心內容,用於控制目標進程調用API函數。

  ·LoadLibrary - 目標進程通過調用此函數來加載病毒DLL。

 在此我只給出了簡要的函數說明,關於函數的詳細功能和介紹請參閱MSDN。

示例程序

  我將在以下的篇幅中用一個簡單的示例Virus.exe來實現這一技術。這個示例的界面如下圖:


(1)首先運行Target.exe,這個文件是一個用Win32 Application嚮導生成的“Hello, World”程序,用來作爲寄生的目標進程。

(2)然後在界面的編輯控件中輸入進程的名稱“Target.exe”,單擊“注入DLL”按鈕,這時候Virus.exe就會將當前目錄下的DLL.dll注入至Target.exe進程中。

(3)在注入DLL.dll之後,你也可以單擊“卸載DLL”來將已經注入的DLL卸載。

  模擬的病毒體DLL.dll

  這是一個簡單的Win32 DLL程序,它僅由一個入口函數DllMain組成:

  1. BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )  
  2. {  
  3.  switch ( fdwReason )  
  4.  {  
  5.   case DLL_PROCESS_ATTACH:  
  6.   {  
  7.    MessageBox( NULL, _T("DLL已進入目標進程。"), _T("信息"), MB_ICONINFORMATION );  
  8.   }  
  9.   break;  
  10.   case DLL_PROCESS_DETACH:  
  11.   {  
  12.    MessageBox( NULL, _T("DLL已從目標進程卸載。"), _T("信息"), MB_ICONINFORMATION );  
  13.   }  
  14.   break;  
  15.  }  
  16.  return TRUE;  
  17. }   


如你所見,這裏我在DLL被加載和卸載的時候調用了MessageBox,這是用來顯示我的遠程注入/卸載工作是否成功完成。而對於一個真正的病毒體來說,
它往往就是處理DLL_PROCESS_ATTACH事件,在其中加入了啓動病毒代碼的部分:

  1. case DLL_PROCESS_ATTACH:  
  2. {  
  3.  StartVirus();  
  4. }  
  5. break;   

注入!
現在要開始我們的注入工作了。首先,我們需要找到目標進程:

  1. DWORD FindTarget(LPCTSTR lpszProcess)  
  2. {  
  3.  DWORD dwRet = 0;  
  4.  HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  
  5.  PROCESSENTRY32 pe32;  
  6.  pe32.dwSize = sizeof( PROCESSENTRY32 );  
  7.  Process32First(hSnapshot, &pe32);  
  8.  do  
  9.  {  
  10.   if ( lstrcmpi(pe32.szExeFile, lpszProcess) == 0 )  
  11.   {  
  12.    dwRet = pe32.th32ProcessID;  
  13.    break;  
  14.   }  
  15.  } while (Process32Next(hSnapshot, &pe32));  
  16.  CloseHandle( hSnapshot );  
  17.  return dwRet;  
  18. }   


這裏我使用了Tool Help函數庫,當然如果你是NT系統的話,也可以選擇PSAPI函數庫。
這段代碼的目的就是通過給定的進程名稱來在當前系統中查找相應的進程,並返回該進程的ID。得到進程ID後,就可以調用OpenProcess來打開目標進程了:

  1. // 打開目標進程  
  2. HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |  
  3.  PROCESS_VM_WRITE, FALSE, dwProcessID );   


現在有必要說一下OpenProcess第一個參數所指定的三種權限。在Win32系統下,每個進程都擁有自己的4G虛擬地址空間,各個進程之間都相互獨立。如果一個進程需要完成跨進程的工作的話,那麼它必須擁有目標進程的相應操作權限。

在這裏,PROCESS_CREATE_THREAD表示我可以通過返回的進程句柄在該進程中創建新的線程,也就是調用CreateRemoteThread的權限

同理,PROCESS_VM_OPERATION則表示在該進程中分配/釋放內存的權限,也就是調用VirtualAllocEx/VirtualFreeEx的權限
PROCESS_VM_WRITE表示可以向該進程的地址空間寫入數據,也就是調用WriteProcessMemory的權限

至此目標進程已經打開,那麼我們該如何來將DLL注入其中呢?在這之前,我請你看一行代碼,是如何在本進程內顯式加載DLL的:

  1. HMODULE hDll = LoadLibrary( "DLL.dll" );   

那麼,如果能控制目標進程調用LoadLibrary,不就可以完成DLL的遠程注入了麼?的確是這樣,我們可以通過CreateRemoteThread將LoadLibrary作爲目標進程的一個線程來啓動,這樣就可以完成“控制目標進程調用LoadLibrary”的工作了。到這裏,也許你會想當然地寫下類似這樣的代碼:

  1. DWORD dwID;  
  2. LPVOID pFunc = LoadLibraryA;  
  3. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,  
  4.  (LPTHREAD_START_ROUTINE)pFunc,   
  5. (LPVOID)"DLL.dll", 0, &dwID );   


不過結果肯定會讓你大失所望——注入DLL失敗

那麼現在讓我們來分析一下失敗的原因吧。我是前說過,在Win32系統下,每個進程都擁有自己的4G虛擬地址空間,各個進程之間都是相互獨立的。在這裏,我們當作參數傳入的字符串"DLL.dll"其實是一個數值,它表示這個字符串位於Virus.exe地址空間之中的地址而這個地址在傳給Target.exe之後,它指向的東西就失去了有效性

舉個例子來說,譬如A、B兩棟大樓,我住在A樓的401;
那麼B樓的401住的是誰我當然不能確定——也就是401這個門牌號在B樓失去了有效性,而且如果我想要入住B樓的話,
我就必須請B樓的樓長爲我在B樓中安排新的住處(當然這個新的住處是否401也就不一定了)。

  由此看來,我就需要做這麼一系列略顯繁雜的手續——

首先在Target.exe目標進程中分配一段內存空間;
然後向這段空間寫入我要加載的DLL名稱;

最後再調用CreateRemoteThread。

這段代碼就成了這樣:

  1. // 向目標進程地址空間寫入DLL名稱  
  2. DWORD dwSize, dwWritten;  
  3. dwSize = lstrlenA( lpszDll ) + 1;  
  4. LPVOID lpBuf = VirtualAllocEx( hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE );  
  5. if ( NULL == lpBuf )  
  6. {  
  7.  CloseHandle( hProcess );  
  8.  // 失敗處理  
  9. }  
  10. if ( WriteProcessMemory( hProcess, lpBuf, (LPVOID)lpszDll, dwSize, &dwWritten ) )  
  11. {  
  12.  // 要寫入字節數與實際寫入字節數不相等,仍屬失敗  
  13.  if ( dwWritten != dwSize )  
  14.  {  
  15.   VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  16.   CloseHandle( hProcess );  
  17.   // 失敗處理  
  18.  }  
  19. }  
  20. else  
  21. {  
  22.  CloseHandle( hProcess );  
  23.  // 失敗處理  
  24. }  
  25. // 使目標進程調用LoadLibrary,加載DLL  
  26. DWORD dwID;  
  27. LPVOID pFunc = LoadLibraryA;  
  28. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,   
  29. (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );   


需要說的有兩點:

第一,由於我要在目標進程中爲ANSI字符串來分配內存空間,所以這裏凡是和目標進程相關的部分,都明確使用了後綴爲“A”的API函數——當然,如果要使用Unicode字符串的話,可以換作後綴是“W”的API;
第二,在這裏LoadLibrary的指針我是取的本進程的LoadLibraryA的地址,這是因爲LoadLibraryA/LoadLibraryW位於kernel32.dll之中,
而Win32下每個應用程序都會把kernel32.dll加載到進程地址空間中一個固定的地址,所以這裏的函數地址在Target.exe中也是有效的

在調用LoadLibrary完畢之後,我們就可以做收尾工作了:

  1. // 等待LoadLibrary加載完畢  
  2. WaitForSingleObject( hThread, INFINITE );  
  3. // 釋放目標進程中申請的空間  
  4. VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  5. CloseHandle( hThread );  
  6. CloseHandle( hProcess );   


在此解釋一下WaitForSingleObject一句。由於我們是通過CreateRemoteThread在目標進程中另外開闢了一個LoadLibrary的線程,所以我們必須等待這個線程運行完畢才能夠釋放那段先前申請的內存。

好了,現在你可以嘗試着整理這些代碼並編譯運行。運行Target.exe,然後開啓一個有模塊查看功能的進程查看工具(在這裏我使用我的July)來查看Target.exe的模塊,
你會發現在注入DLL之前,Target.exe中並沒有DLL.dll的存在:

 在調用了注入代碼之後,DLL.dll就位於Target.exe的模塊列表之中了:

二、矛盾相生(解毒)

1、法一:與DLL注入過程類似

記得2004年初我將QQ尾巴病毒成功仿真後,有很多網友詢問我如何才能殺毒,不過我都沒有回答——因爲當時我研究的重點並非病毒的寄生特性。這一寄生特性直到今天可以說我才仿真完畢,那麼,我就將解毒的方法也一併公開吧。

和DLL的注入過程類似,只不過在這裏使用了兩個API:GetModuleHandle和FreeLibrary。出於篇幅考慮,我略去了與注入部分相似或相同的代碼:

  1. // 使目標進程調用GetModuleHandle,獲得DLL在目標進程中的句柄  
  2. DWORD dwHandle, dwID;  
  3. LPVOID pFunc = GetModuleHandleA;  
  4. HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)  
  5. pFunc, lpBuf, 0, &dwID );  
  6. // 等待GetModuleHandle運行完畢  
  7. WaitForSingleObject( hThread, INFINITE );  
  8. // 獲得GetModuleHandle的返回值  
  9. GetExitCodeThread( hThread, &dwHandle );  
  10. // 釋放目標進程中申請的空間  
  11. VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );  
  12. CloseHandle( hThread );  
  13. // 使目標進程調用FreeLibrary,卸載DLL  
  14. pFunc = FreeLibrary;  
  15. hThread = CreateRemoteThread( hProcess, NULL, 0,  
  16.  (LPTHREAD_START_ROUTINE)pFunc,  
  17.  (LPVOID)dwHandle, 0, &dwID );  
  18. // 等待FreeLibrary卸載完畢  
  19. WaitForSingleObject( hThread, INFINITE );  
  20. CloseHandle( hThread );  
  21. CloseHandle( hProcess );   

用這個方法可以卸載一個進程中的DLL模塊,當然包括那些非病毒體的DLL。所以,這段代碼還是謹慎使用爲好

在完成卸載之後,如果沒有別的程序加載這個DLL,你就可以將它刪除了。

2、法二:採用查殺工具

2、1.安裝木馬查殺工具。對於多數人而言,要想通過手工查殺DLL木馬是不太現實的,因此安裝一款可以查殺此類木馬的反病毒軟件,是非常有必要的。這裏還要提醒讀者的是,應及時升級病毒數據庫,這樣才能保證有效地查殺絕大部分木馬病毒。當然,我們也可以在計算機中安裝那些專門針對木馬的查殺工具,例如木馬克星。

2、2.查看是否有不明端口開放以及對端口通信進行監控。只要木馬進行連接,接受/發送數據則必然會打開端口,DLL木馬也不例外,我們可以通過“Netstat-ano”命令來查看TCP/UDP端口的連接,以及開放端口的進程標識符;

也可以直接使用進程端口查看工具Fport.exe來查看與端口對應的進程,以發現是否有不明的連接和端口開放。另外,有些DLL木馬通過端口劫持或者端口重用的方法來進行通信,所以僅是查看端口還是不夠的,有必要的話,我們可使用嗅探器來了解打開的端口到底在傳輸些什麼數據。

2、3.檢查系統目錄下是否有可疑的DLL文件。安裝好系統和所有應用程序之後,可對系統目錄下System32文件夾中的EXE和DLL文件作一記錄:在命令提示符下執行“dir*.exe>bak1.txt&dir*.dll>bak2.txt”,將所有的EXE和DLL文件信息導出成TXT文件保存。當日後發現異常時,可以使用相同的命令再次備份,並使用FC命令比較兩次的EXE文件和DLL文件。通過這種方法,我們可以發現可疑的EXE和DLL文件,同時通過文件的大小、創建時間來判斷是否爲DLL木馬。

2、4.查看系統進程調用的DLL文件。當我們懷疑有DLL木馬插入到系統進程,可以使用一些第三方進程工具來查看進程所調用的DLL文件,然後進一步確認是否中了DLL木馬。此類查看工具有進程獵手、進程間諜等等。另外,我們也可以使用XP系統自帶的命令行工具TaskList,來顯示進程調用的DLL文件,並將這些信息導出成TXT文件保存,以便隨時進行比較。


但我們就要利用這種木馬 因爲他比較難查殺 所以\~~~~~~~~~~~~~~

3、法三:找出寄生的EXE

警報,病毒無法清除

如果系統中被植入了DLL木馬,將會出現什麼情況呢?

首先,系統被黑客遠程控制,出現數據丟失等情況;其次,殺毒軟件會報警系統中有病毒,但是卻無法進行清除。例如,在電腦中運行了上面製作的上興遠控木馬後,筆者電腦上安裝的殺毒軟件提示發現病毒,但是在刪除DLL病毒文件時卻失敗了。即使我們手工刪除病毒文件,也會因爲DLL文件正在使用中,所以也無法徹底刪除。即使進入安全模式,得到的也是同樣的結果。
另外,由於DLL木馬是插入到系統進程中的,因此通過任務管理器等進程工具,也無法發現任何木馬進程,這給木馬的清除帶來了很大的困難!
無法刪除病毒文件,無法查找到木馬進程,那麼到底該如何清除DLL注入式木馬呢?

無所遁形,揪出木馬藏身之所

雖然木馬沒有自己的進程,但是有一個宿主進程,只要結束宿主進程,停止DLL文件的調用,就可以刪除DLL文件,進而清除木馬。因此,清除DLL木馬的第一步,就是找到木馬注入的宿主進程。那麼,如何才能找到木馬注入的宿主進程呢?且讓我們細細看來。

以清除“上興遠控木馬”爲例,從殺毒軟件的報警提示中已經知道木馬DLL文件名爲“rejoice.dll”。因此,就可以通過一些查看進程調用DLL文件的進程管理工具,找到該文件的宿主進程,此類工具很多,比如大名鼎鼎的ICESword

運行IceSword後,點擊左側邊欄“查看→進程”,就可以在其右側窗口中看到所有進程列表。右鍵點擊某進程,在彈出菜單中選擇“模塊信息”命令,即可看到該進程調用的所有DLL文件


提示:DLL注入式木馬通常是將DLL文件,加載到explorer.exe、svchost.exe、winlogon.exe、iexplore.exe等系統進程中的。因此在查找DLL宿主文件時,可以關閉其它無關的程序,然後依次檢查這幾個進程中的DLL文件。

不過一個一個的檢查系統進程,確實有些麻煩,如何才能快速的定位木馬的宿主進程呢?可以使用一款名爲“procexp”的進程管理工具。運行procexp後,在程序界面中間顯示的是樹狀進程關係列表,下方是每個進程的詳細信息。點擊菜單“Find→Find DLL”命令,打開DLL文件查找對話框,在“DLL Substring”中輸入要查找的關鍵詞,這裏輸入剛纔殺毒軟件掃描出的DLL文件名“rejoice.dll”。然後點擊“Search”按鈕,在下方的列表中就可以看到該DLL文件是被哪個進程調用的了。


牛刀小試,清除普通進程DLL注入木馬

對於大部分DLL注入木馬,其注入到“iexplore.exe”和“explorer.exe”這兩個進程。對於這類普通進程的DLL木馬,清除將是非常方便的。

如果DLL文件是注入到“iexplore.exe”進程中,由於此進程就是IE瀏覽器進程,因此就需要先關掉所有IE窗口和相關程序,然後直接找到DLL文件進行刪除就可以了。

如果DLL文件是注入到“explorer.exe”進程中,那麼就略顯麻煩一些。由於此進程用於顯示桌面和資源管理器,因此,當通過任務管理器結束掉“explorer.exe”進程時,桌面無法看到,桌面上所有圖標消失掉,"我的電腦"、"網上鄰居"等所有圖標都不見了,同時,也無法打開資源管理器找到木馬文件進行刪除。怎麼辦呢?

實際上,解決的方法也很簡單。在任務管理器中點擊菜單“文件→新任務運行”,打開“創建新任務”對話框,點擊“瀏覽”按鈕,通過瀏覽對話框就可以打開DLL文件所在的路徑。然後選擇“文件類型”爲“所有文件”,即可顯示並刪除DLL文件了。


在瀏覽對話框中刪除DLL文件

除惡務盡,清除特殊DLL注入木馬

除了以上所說的注入普通進程的DLL木馬之外,還有許多木馬注入到系統裏關鍵進程中,比如svchost.exe、smss.exe、winlogon.exe進程。這些進程使用普通方式無法結束,使用特殊工具結束掉進程後,卻又很可能造成系統崩潰無法正常運行的情況。對於這些木馬,我們可以通過以下兩種方法進行清除。

1.使用IceSword卸載DLL文件

IceSword的功能十分強大,我們曾在以前作過介紹,在這裏,就可以利用它卸載掉已經插入到正在運行的系統進程中的DLL文件。在IceSword的進程列表顯示窗口中,右鍵點擊DLL木馬宿主進程,選擇彈出命令“模塊信息”。在進程模塊信息對話框中找到DLL木馬文件,選擇文件後點擊“強制解除”命令,即可將系統進程中的DLL木馬文件卸載掉了


卸載系統進程中的DLL木馬

4、法四:定時枚舉,發現不是本身所用的就強制Free掉

防止DLL注入我用的是另一種方法:

//允許的模塊列表
theApp.m_szAuthorizedList = " USER32.DLL RPCRT4.DLL KERNEL32.DLL GDI32.DLL ADVAPI32.DLL NTDLL.DLL MSVCRT.DLL SETUPAPI.DLL CFGMGR32.DLL WINMM.DLL SHELL32.DLL SHLWAPI.DLL MSVCR80.DLL MFC80.DLL OLE32.DLL COMCTL32.DLL OLEACC.DLL OLEAUT32.DLL ATL80.DLL MSVCM80.DLL MFC80CHS.DLL MSVCP80.DLL MFCM80.DLL MFCM80U.DLL MFC80U.DLL VCOMP.DLL SECUR32.DLL MSVCRT.DLL MSVCP60.DLL MFC60.DLL ";

然後定時枚舉系統進程,找到自己進程本身並分析所加載的模塊,發現不是本身所用的就強制FREE掉:

  1. do  
  2. {  
  3.     if(!IsModuleAuthorized(me32.szModule))  
  4.     {  
  5.         HMODULE hmodule = me32.hModule;  
  6.         CloseHandle(hModuleSnap);    
  7.         FreeLibrary(hmodule);  
  8.         //發現可疑模塊並已移除  
  9.         return;  
  10.     }  
  11. }  

 

5、法五: 攔截自身LoadLibraryExW 這個函數吧  

如果僅僅只是防禦 遠程線程 你可以按下面這樣做  :

(1)在程序初始化時把自身程序的所有線程ID 用個ULONG 數組保存起來   
並動態維護這個ULONG數組 這個也是多線程程序所必需的吧 如果是單線程就更方便了
(2)然後 在LoadLibraryExW假函數裏做一個判斷   
判斷當前的操作線程是否爲自身程序原有的線程 如果不是 就是遠程線程了   

  1. ULONG __stdcall Fake_LoadLibraryExW(LPCWSTR lpwLibFileName,HANDLE hFile,DWORD dwFlags)     
  2. {  
  3.     ULONG TID=GetCurrentThreadId();  
  4.     //把這個TID 循環對比一下 是否存在於自身TID 數組     
  5.   
  6.   
  7.     if (flag)//存在則 通過     
  8.     {  
  9.         return MyReal_LoadLibraryExW(lpwLibFileName,hFile,dwFlags);  
  10.     }  
  11.     else  
  12.     {  
  13.         return FALSE;  
  14.     }  
  15. }  




(4)上面的方法可以防止遠程線程 在自身程序加載DLL ,但還有個問題 值得注意
WINDOWS的 SetWindowsHookEx (設GUI全局鉤子) 這個函數,因爲他是切換到進程上下文 使用程序自身線程來加載DLL的 所以不能用判斷TID的方法來判斷了  

這裏再提供二個思路   

(4、1)一個是 判斷當前加載 的DLL 是否有WINDOWS簽名 是則放行 否則攔截 (稍微有點費時 但非常安全)

(4、2)在一個是 判斷路徑  
系統加載DLL時是不會包含DLL路徑的直接是類似“kernel32.dll” 這樣的   
所以,可以判斷當前路徑是否包含路徑分割符,是則攔截 否則放行 (速度快 安全性較高)


結合上面三種方法 自己寫個規則來決定攔截與否吧
比直接定義模塊名 兼容性高多了 也方便多了   

三、如何防止自己製作的DLL遠程注入被查殺
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章